]> arthur.barton.de Git - netatalk.git/commitdiff
Fix fce merge conflict
authorFrank Lahm <franklahm@googlemail.com>
Tue, 24 May 2011 12:32:02 +0000 (14:32 +0200)
committerFrank Lahm <franklahm@googlemail.com>
Tue, 24 May 2011 12:32:02 +0000 (14:32 +0200)
311 files changed:
Makefile.am
NEWS
README [deleted file]
TODO [deleted file]
VERSION
bin/Makefile.am
bin/ad/.gitignore [new file with mode: 0644]
bin/ad/Makefile.am [new file with mode: 0644]
bin/ad/ad.c [new file with mode: 0644]
bin/ad/ad.h [new file with mode: 0644]
bin/ad/ad_cp.c [new file with mode: 0644]
bin/ad/ad_find.c [new file with mode: 0644]
bin/ad/ad_ls.c [new file with mode: 0644]
bin/ad/ad_mv.c [new file with mode: 0644]
bin/ad/ad_rm.c [new file with mode: 0644]
bin/ad/ad_util.c [new file with mode: 0644]
bin/afile/Makefile.am [deleted file]
bin/afile/achfile.c [deleted file]
bin/afile/afile.c [deleted file]
bin/afile/common.c [deleted file]
bin/afile/common.h [deleted file]
bin/cnid/Makefile.am
bin/cnid/ad.c [deleted file]
bin/cnid/ad.h [deleted file]
bin/cnid/ad_cp.c [deleted file]
bin/cnid/ad_ls.c [deleted file]
bin/cnid/ad_util.c [deleted file]
bin/megatron/asingle.c
bin/megatron/macbin.c
bin/misc/.gitignore
bin/misc/Makefile.am
bin/misc/logger_test.c [new file with mode: 0644]
bin/misc/uuidtest.c
bin/uniconv/uniconv.c
config/AppleVolumes.default.tmpl
config/Makefile.am
config/afpd.conf.tmpl
config/netatalk.conf
configure.in
contrib/ICDumpSuffixMap [deleted file]
contrib/Makefile.am
contrib/macusers/macusers.in
contrib/misc/make-casetable.pl [new file with mode: 0755]
contrib/misc/make-precompose.h.pl [changed mode: 0644->0755]
contrib/patches/README [deleted file]
contrib/patches/patch.afp_vfs [deleted file]
contrib/patches/patch.mangled_trash_with_ip [deleted file]
contrib/patches/patch.samba.3.0.5pre2-SVN [deleted file]
contrib/patches/patch.samba.3.0a20 [deleted file]
contrib/patches/patch.vfs [deleted file]
contrib/permtest/add_permtest.patch [deleted file]
contrib/permtest/permtest.cfg [deleted file]
contrib/permtest/permtest.pl.in [deleted file]
contrib/shell_utils/Makefile.am
contrib/shell_utils/apple_cp.in [deleted file]
contrib/shell_utils/apple_mv.in [deleted file]
contrib/shell_utils/apple_rm.in [deleted file]
contrib/shell_utils/asip-status.pl.in
distrib/debian/README.Debian [deleted file]
distrib/debian/changelog [deleted file]
distrib/debian/control [deleted file]
distrib/debian/copyright [deleted file]
distrib/debian/cvs2deb.sh [deleted file]
distrib/debian/logcheck/ignore.d.server [deleted file]
distrib/debian/logcheck/violations.ignore.d [deleted file]
distrib/debian/netatalk-dev.docs [deleted file]
distrib/debian/netatalk-dev.files [deleted file]
distrib/debian/netatalk.dirs [deleted file]
distrib/debian/netatalk.docs [deleted file]
distrib/debian/netatalk.examples [deleted file]
distrib/debian/netatalk.files [deleted file]
distrib/debian/netatalk.init [deleted file]
distrib/debian/netatalk.links [deleted file]
distrib/debian/netatalk.undocumented [deleted file]
distrib/debian/patches/add_printer.patch [deleted file]
distrib/debian/patches/etc2ps.sh.patch [deleted file]
distrib/debian/patches/filterdir.patch [deleted file]
distrib/debian/patches/netatalk.conf.patch [deleted file]
distrib/debian/patches/netatalk.pamd.patch [deleted file]
distrib/debian/patches/psf.8.patch [deleted file]
distrib/debian/rules [deleted file]
distrib/initscripts/Makefile.am
distrib/initscripts/rc.atalk.bsd.tmpl
distrib/initscripts/rc.atalk.debian.tmpl
distrib/initscripts/rc.atalk.gentoo.tmpl
distrib/initscripts/rc.atalk.redhat.tmpl
distrib/initscripts/rc.atalk.suse.tmpl
distrib/initscripts/rc.atalk.sysv.tmpl
distrib/rpm/buildrpm [deleted file]
distrib/rpm/netatalk-asun.spec.old [deleted file]
distrib/rpm/netatalk-fedora.spec [deleted file]
distrib/rpm/netatalk-mandrake.spec [deleted file]
distrib/rpm/netatalk-redhat.spec [deleted file]
distrib/rpm/netatalk-rpmbuild.patch [deleted file]
doc/DEVELOPER
doc/FAQ [deleted file]
doc/Makefile.am
doc/README.ACLs [deleted file]
doc/README.documentation [deleted file]
doc/README.hidden-items [deleted file]
doc/README.ids [deleted file]
doc/TODO2.1 [deleted file]
etc/Makefile.am
etc/afpd/Makefile.am
etc/afpd/acl_mappings.h
etc/afpd/acls.c
etc/afpd/acls.h
etc/afpd/afp_avahi.c [new file with mode: 0644]
etc/afpd/afp_avahi.h [new file with mode: 0644]
etc/afpd/afp_config.c
etc/afpd/afp_config.h
etc/afpd/afp_dsi.c
etc/afpd/afp_options.c
etc/afpd/afp_zeroconf.c [new file with mode: 0644]
etc/afpd/afp_zeroconf.h [new file with mode: 0644]
etc/afpd/appl.c
etc/afpd/auth.c
etc/afpd/catsearch.c
etc/afpd/desktop.c
etc/afpd/dircache.c [new file with mode: 0644]
etc/afpd/dircache.h [new file with mode: 0644]
etc/afpd/directory.c
etc/afpd/directory.h
etc/afpd/enumerate.c
etc/afpd/file.c
etc/afpd/file.h
etc/afpd/filedir.c
etc/afpd/fork.c
etc/afpd/fork.h
etc/afpd/globals.h
etc/afpd/hash.c
etc/afpd/main.c
etc/afpd/mangle.c
etc/afpd/messages.c
etc/afpd/ofork.c
etc/afpd/status.c
etc/afpd/switch.c
etc/afpd/uam.c
etc/afpd/unix.c
etc/afpd/volume.c
etc/afpd/volume.h
etc/atalkd/nbp.c
etc/cnid_dbd/Makefile.am
etc/cnid_dbd/cmd_dbd.c
etc/cnid_dbd/cmd_dbd.h
etc/cnid_dbd/cmd_dbd_scanvol.c
etc/cnid_dbd/cnid_metad.c
etc/cnid_dbd/comm.c
etc/cnid_dbd/comm.h
etc/cnid_dbd/db_param.c
etc/cnid_dbd/db_param.h
etc/cnid_dbd/dbd.h
etc/cnid_dbd/dbd_add.c
etc/cnid_dbd/dbd_getstamp.c
etc/cnid_dbd/dbd_resolve.c
etc/cnid_dbd/dbd_search.c [new file with mode: 0644]
etc/cnid_dbd/dbd_update.c
etc/cnid_dbd/dbif.c
etc/cnid_dbd/dbif.h
etc/cnid_dbd/main.c
etc/cnid_dbd/pack.c
etc/cnid_dbd/pack.h
etc/cnid_dbd/usockfd.c
etc/uams/uams_dhx2_pam.c
etc/uams/uams_dhx2_passwd.c
include/atalk/Makefile.am
include/atalk/acl.h
include/atalk/adouble.h
include/atalk/afp.h
include/atalk/bstradd.h [new file with mode: 0644]
include/atalk/bstrlib.h [new file with mode: 0644]
include/atalk/cnid.h
include/atalk/cnid_dbd_private.h
include/atalk/cnid_private.h
include/atalk/directory.h
include/atalk/dsi.h
include/atalk/ea.h
include/atalk/errchk.h [new file with mode: 0644]
include/atalk/ftw.h [new file with mode: 0644]
include/atalk/ldapconfig.h
include/atalk/paths.h
include/atalk/queue.h [new file with mode: 0644]
include/atalk/server_child.h
include/atalk/server_ipc.h
include/atalk/unicode.h
include/atalk/util.h
include/atalk/uuid.h
include/atalk/vfs.h
include/atalk/volinfo.h
include/atalk/volume.h
libatalk/Makefile.am
libatalk/acl/Makefile.am
libatalk/acl/aclldap.h
libatalk/acl/cache.c
libatalk/acl/ldap.c
libatalk/acl/ldap_config.c
libatalk/acl/unix.c [new file with mode: 0644]
libatalk/acl/uuid.c
libatalk/adouble/ad_attr.c
libatalk/adouble/ad_date.c
libatalk/adouble/ad_flush.c
libatalk/adouble/ad_lock.c
libatalk/adouble/ad_open.c
libatalk/adouble/ad_write.c
libatalk/asp/asp_getsess.c
libatalk/bstring/.gitignore [new file with mode: 0644]
libatalk/bstring/Makefile.am [new file with mode: 0644]
libatalk/bstring/bstradd.c [new file with mode: 0644]
libatalk/bstring/bstrlib.c [new file with mode: 0644]
libatalk/cnid/cdb/Makefile.am
libatalk/cnid/cdb/cnid_cdb_open.c
libatalk/cnid/cnid.c
libatalk/cnid/dbd/cnid_dbd.c
libatalk/cnid/dbd/cnid_dbd.h
libatalk/compat/pselect.c
libatalk/dsi/README
libatalk/dsi/dsi_attn.c
libatalk/dsi/dsi_close.c
libatalk/dsi/dsi_getsess.c
libatalk/dsi/dsi_opensess.c
libatalk/dsi/dsi_read.c
libatalk/dsi/dsi_stream.c
libatalk/dsi/dsi_tcp.c
libatalk/dsi/dsi_tickle.c
libatalk/unicode/.gitignore
libatalk/unicode/Makefile.am
libatalk/unicode/precompose.h
libatalk/unicode/ucs2_casetable.h [deleted file]
libatalk/unicode/utf16_case.c [new file with mode: 0644]
libatalk/unicode/utf16_casetable.h [new file with mode: 0644]
libatalk/unicode/utf8.c
libatalk/unicode/util_unistr.c
libatalk/util/Makefile.am
libatalk/util/cnid.c [new file with mode: 0644]
libatalk/util/fault.c
libatalk/util/ftw.c [new file with mode: 0644]
libatalk/util/logger.c
libatalk/util/queue.c [new file with mode: 0644]
libatalk/util/server_child.c
libatalk/util/server_ipc.c
libatalk/util/socket.c
libatalk/util/test/.gitignore [deleted file]
libatalk/util/test/Makefile.am [deleted file]
libatalk/util/test/logger_test.c [deleted file]
libatalk/util/unix.c
libatalk/util/volinfo.c
libatalk/vfs/Makefile.am
libatalk/vfs/acl.c
libatalk/vfs/ea.c
libatalk/vfs/ea_sys.c
libatalk/vfs/sys_ea.c
libatalk/vfs/unix.c
libatalk/vfs/vfs.c
macros/db3-check.m4
macros/summary.m4
macros/zeroconf.m4 [new file with mode: 0644]
man/man1/.gitignore
man/man1/Makefile.am
man/man1/achfile.1 [deleted file]
man/man1/ad.1
man/man1/aecho.1
man/man1/afpldaptest.1.tmpl [new file with mode: 0644]
man/man1/afppasswd.1
man/man1/apple_cp.1.tmpl [deleted file]
man/man1/apple_dump.1
man/man1/apple_mv.1.tmpl [deleted file]
man/man1/apple_rm.1.tmpl [deleted file]
man/man1/asip-status.pl.1.tmpl
man/man1/dbd.1
man/man1/getzones.1
man/man1/megatron.1
man/man1/nbp.1
man/man1/netatalk-config.1
man/man1/pap.1
man/man1/psorder.1
man/man1/uniconv.1.tmpl
man/man3/Makefile.am
man/man3/atalk_aton.3
man/man3/nbp_name.3
man/man4/Makefile.am
man/man4/atalk.4
man/man5/AppleVolumes.default.5.tmpl
man/man5/Makefile.am
man/man5/afp_ldap.conf.5.tmpl
man/man5/afp_signature.conf.5.tmpl
man/man5/afp_voluuid.conf.5.tmpl [new file with mode: 0644]
man/man5/afpd.conf.5.tmpl
man/man5/atalkd.conf.5.tmpl
man/man5/netatalk.conf.5.tmpl
man/man5/papd.conf.5.tmpl
man/man8/Makefile.am
man/man8/afp_acls.8.tmpl [deleted file]
man/man8/afpd.8.tmpl
man/man8/atalkd.8.tmpl
man/man8/cnid_dbd.8.tmpl
man/man8/cnid_metad.8.tmpl
man/man8/papd.8.tmpl
man/man8/papstatus.8.tmpl
man/man8/psf.8.tmpl
man/man8/timelord.8
test/.gitignore [new file with mode: 0644]
test/Makefile.am [new file with mode: 0644]
test/afpd/.gitignore [new file with mode: 0644]
test/afpd/Makefile.am [new file with mode: 0644]
test/afpd/afpfunc_helpers.c [new file with mode: 0644]
test/afpd/afpfunc_helpers.h [new file with mode: 0644]
test/afpd/subtests.c [new file with mode: 0644]
test/afpd/subtests.h [new file with mode: 0644]
test/afpd/test.c [new file with mode: 0644]
test/afpd/test.h [new file with mode: 0644]
test/afpd/test.sh [new file with mode: 0755]

index 44ac045379515e1f79b5a26cf098127308ebf3c8..1203023118901ebe8c00249ef3cc2889222fe104 100644 (file)
@@ -1,9 +1,8 @@
 # Makefile.am for top level of netatalk package
 
-SUBDIRS = libatalk bin config etc man contrib distrib include sys doc macros
+SUBDIRS = libatalk bin config etc man contrib distrib include sys doc macros test
 
-EXTRA_DIST = CONTRIBUTORS COPYRIGHT COPYING NEWS\
-       TODO VERSION services.atalk
+EXTRA_DIST = CONTRIBUTORS COPYRIGHT COPYING NEWS VERSION services.atalk
 
 ACLOCAL_AMFLAGS = -I macros
 AUTOMAKE_OPTIONS = foreign
diff --git a/NEWS b/NEWS
index 47cd89d76ef972ecadfbe5086f1a3e6d8d51b469..757b22a7d716020af4fa142e7d07d8bfe3c17317 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,138 @@
+Changes in 2.2beta4
+===================
+
+* NEW: afpd: new afpd.conf options "tcprcvbuf" and "tcpsndbuf" to customize
+       the corresponding TCP socket options.
+* NEW: afpd: new afpd.conf option "nozeroconf" which disabled automatic
+       Zeroconf service registration.
+* FIX: afpd: generate mersenne primes for DHX2 UAM once at startup,
+       not for every login
+* FIX: afpd: DSI streaming deadlock
+* FIX: afpd: extended sleep
+* FIX: afpd: directory cache
+* FIX: Support for platforms that do not have the *at functions
+* UPD: afpd: put POSIX write lock on volume files while reading them
+
+Changes in 2.2beta3
+===================
+
+* FIX: afpd: fix option volsizelimit to return a usefull value for the
+       volume free space using `du -sh` with popen
+* FIX: afpd: fix idle connection disconnects
+* FIX: afpd: don't disconnect sessions for clients if boottimes don't match
+* FIX: afpd: better handling of very long filenames that contain many
+       multibyte UTF-8 glyphs
+
+Changes in 2.2beta2
+====================
+
+* NEW: afpd: AFP 3.3
+* UPD: afpd: AFP 3.x can't be disabled
+
+Changes in 2.2beta1
+====================
+
+* FIX: composition of Surrogate Pair
+* UPD: gentoo,suse,cobalt,tru64: inistscript name is "netatalk", not "atalk"
+* UPD: gentoo: rc-update install don't hook in the Makefile
+
+Changes in 2.2alpha5
+====================
+
+* UPD: afpd: new option "searchdb" which enables fast catalog searches
+       using the CNID db.
+* UPD: Case-insensitive fast search with the CNID db
+* UPD: cnid_dbd: afpd now passes the volume path, not the db path when
+       connecting for a volume. cnid_dbd will read the
+       ".AppleDesktop/.volinfo" file of the volume in order to figure
+       out the CNID db path and the volume charset encoding.
+
+Changes in 2.2alpha4
+====================
+
+* NEW: Enhanced CNID "dbd" database for fast name search support.
+       Important: this makes cnidscheme "cdb" incompatible with "dbd".
+* NEW: afpd: support for fast catalog searches
+* NEW: ad utility: ad find
+* UPD: afpd: CNID database versioning check for "cdb" scheme
+* UPD: cnid_dbd: CNID database versioning and upgrading. Additional
+       CNID database index for fast name searches.
+
+Changes in 2.2alpha3
+====================
+
+* FIX: afpd: various fixes
+* FIX: Any daemon did not run if atalkd doesn't exist (redhat/debian)
+
+Changes in 2.2alpha2
+====================
+
+* FIX: afpd: fix compilation error when ACL support is not available
+* FIX: Ensure Appletalk manpages and config files are distributed
+
+Changes in 2.2alpha1
+====================
+
+* NEW: ad utility: ad cp
+* NEW: ad utility: ad rm
+* NEW: ad utility: ad mv
+* NEW: afpd: dynamic directoy and CNID cache (new config option -dircachesize)
+* NEW: afpd: POSIX 1e ACL support
+* NEW: afpd: automagic Zeroconf registration with avahi, registering both
+       the service _afpovertcp._tcp and TimeMachine volumes with _adisk._tcp.
+* UPD: afpd: ACLs usable (though not visible on the client side) without common
+       directory service, by mapping ACLs to UARight
+* UPD: afpd: performance improvements for ACL access calculations
+* UPD: AppleTalk is disabled by default at configuration time. If needed
+       use configure switch --enable-ddp.
+* FIX: afpd: Solaris 10 compatibilty fix: don't use SO_SNDTIMEO/SO_RCVTIMEO,
+       use non-blocking IO and select instead.
+* FIX: cnid_dbd: Solaris 10 compatibilty fix: don't use SO_SNDTIMEO/SO_RCVTIMEO,
+       use non-blocking IO and select instead.
+* REM: afile/achfile/apple_cp/apple_mv/apple_rm: use ad
+
+Changes in 2.1.6
+================
+
+* FIX: afpd: Fix for LDAP user cache corruption
+* FIX: afpd: Fix for not shown ACLs for when filesyem uid or gid
+       couldn't be resolved because (eg deleted users/groups)
+* FIX: gentoo: cannot set $CNID_CONFIG
+* FIX: ubuntu: servername was empty
+* FIX: Solaris: configure script failed to enable DDP module
+* FIX: AppleDouble buffer overrun by extremely long filename
+* UPD: afpd: return version info with machine type in DSIGetStatus
+* UPD: dbd: use on-disk temporary rebuild db instead of in-memory db
+
+Changes in 2.1.5
+================
+
+* UPD: afpd: support newlines in -loginmesg with \n escaping syntax
+* UPD: afpd: support for changed chmod semantics on ZFS with ACLs
+       in onnv145+
+* FIX: afpd: fix leaking ressource when moving objects on the server
+* FIX: afpd: backport Solaris 10 compatibilty fix from 2.2: don't use
+       SO_SNDTIMEO/SO_RCVTIMEO, use non-blocking IO and select instead.
+* FIX: afpd: misaligned memory access on Sparc in ad_setattr, fixes
+       bug 3110004.
+* FIX: cnid_dbd: backport Solaris 10 compatibilty fix from 2.2: don't
+       use SO_SNDTIMEO/SO_RCVTIMEO, use non-blocking IO and select instead.
+
+Changes in 2.1.4
+================
+
+* FIX: afpd: Downstream fix for FreeBSD PR 148022
+* FIX: afpd: Fixes for bugs 3074077 and 3074078
+* FIX: afpd: Better handling of symlinks in combination with ACLs and EAs.
+       Fixes bug 3074076.
+* FIX: dbd: Adding a file with the CNID from it's adouble file did
+       not work in case that CNID was already occupied in the database
+* FIX: macusers: add support for Solaris
+* NEW: cnid_metad: use a PID lockfile
+* NEW: afpd: prevent log flooding
+* UPD: dbd: ignore ".zfs" snapshot directories
+* UPD: dbd: support interrupting -re mode
+
 Changes in 2.1.3
 ================
 
@@ -43,6 +178,7 @@ Changes in 2.1-release
 
 Changes in 2.1-beta2
 ====================
+
 * NEW: afpd: static generated AFP signature stored in afp_signature.conf,
        cf man 5 afp_signature.conf
 * NEW: afpd: clustering support: new per volume option "cnidserver".
diff --git a/README b/README
deleted file mode 100644 (file)
index 5d09f07..0000000
--- a/README
+++ /dev/null
@@ -1,16 +0,0 @@
-The documentation for Netatalk is arranged as follows:
-
-* doc/Netatalk-Manual(.pdf|.txt)
-  the Netatalk manual
-* doc/DEVELOPER 
-  information for developers and additional requirements for compiling
-* doc/FAQ
-  FAQ in progress
-* doc/README.AppleTalk
-  additional instructions for AppleTalk on various operating systems.
-* doc/README.hidden-items
-  documents the various special files created by netatalk in file shares
-
-This should be all you need to get netatalk running.
-
-        - The Netatalk Team
diff --git a/TODO b/TODO
deleted file mode 100644 (file)
index 2c9540c..0000000
--- a/TODO
+++ /dev/null
@@ -1,84 +0,0 @@
-desired features (in no particular order):
-    afpd:
-        return the right version-specific error codes
-        honor the readonly/deleteinhibit/renameinhibit/copyprotect bits.
-        via a database (that handles ro media, -db <path>):
-               Add afp_fileid capabilities - done
-               Add afp_catalogue
-               afp_enumerate optimization
-        server messages in useful places 
-       change afp/ddp child handling to be in line w/ generic?
-        administrative control program (using asip API?)
-       appledouble v2 (gets us persistent DIDs). we'll need a did
-               database for non-hfs volumes. i think mmapping a
-               bitmap with dids at the root level should work.
-               also, v2 gets us much better prodos support and
-               provides for shortname support. - done 
-       various toggles to afpd.conf (-timeout, etc.)
-       figure out more ways of optimizing things. current places
-               of interest:
-               1) reading/writing. currently there's character 
-                  replacement. we should have a better way of doing
-                  it that speeds things up the no-replacement case.
-                  i've already done that for the crlf case.
-               2) use of mmap where appropriate. NOTE: this will only
-                   be a win if we mmap heavily used files. likely
-                  candidates include file-based databases. mmapping 
-                  header files is actually pretty slow under linux.
-               3) change lookup algorithms where appropriate. 
-       ability to interact with hfs desktop databases (either by
-               changing hfs or changing afpd).
-        utmp/wtmp support
-               
-    papd:
-       DHX authenticated printing logins (does a real Appleshare server
-               do this?)
-       Change to samba-style config file.       
-
-    autoconf/automake system:
-       Need to separate out the flags and libraries for different
-               applications, so that we aren't having them all linked with
-               every library, etc.
-        
-things to fix:
-        cleaner separation of layers.
-       AFP <-> Unix permissions. there are a couple cases where they 
-               don't map perfectly. in particular, gid == 0 means
-               ignore group permissions while uid == 0 means anybody
-                can fiddle with this file. in addition, we need to be
-               able to still change permissions on a directory with u-a
-               set. finally, we need to adjust the offspring count
-               for directories based upon what permissions they
-               have. i.e., search -> can see directories. 
-                           read -> can see files.
-               we need to map permissions so that these semantics get
-                followed.
-        support picking up items from dropboxes without linux-side superuser
-                intervention
-        support for system-wide messages; send the main afpd process SIGUSR2
-                and each of the children should send out the same message.
-        replacement of a linefeed with the appropriate Macintosh character in
-                server messaging (currently replaces with a space)
-
-added features:
-       sped up of_findname and of_dealloc.
-        nfs quota support
-       solaris beta STREAMS driver added.
-       64-bit cleanup
-       cleaner startup/takedown
-        added debug messages to dsi_write areas.
-       fixed server info unexpected disconnects (due to OT bug).
-        afp/ddp and afp/tcp cohabitation. afp/ddp and afp/tcp can
-       operate separately now (-T turns off tcp, -D turns off ddp).
-       incorporated the netbsd patches 
-               [source: wrstuden@loki.stanford.edu (Bill Studenmund)]
-       casefolding on a per volume basis.
-       added "generic" platform support for AFP/tcp.
-       :ETCDIR:/afppasswd file for randnum passwds
-       AppleVolumes variable substitions
-       atalkd: zones on single interfaces and the ability to control
-               which interfaces get routes between them.
-       papd:   cleartext and noauth logins using Print Server Security
-               Protocol
-               CAP-style authenticated printing
-               fixed errors when spooling to lpr 0.46 queue
diff --git a/VERSION b/VERSION
index ac2cdeba0137a6c2cbca06867b7ab994a913e294..4b92f18f6f96dfbc2b7957211686ba241ab46e87 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.1.3
+2.2-beta4
\ No newline at end of file
index d23a5e5e2dd8af358672e56ec114c0af66a70f5e..7c633f748f409ed1fecf5ba637ebfa75d7ce572b 100644 (file)
@@ -1,3 +1,11 @@
 # Makefile.am for bin/
 
-SUBDIRS = adv1tov2 aecho afile afppasswd cnid getzones megatron nbp pap psorder uniconv misc
+SUBDIRS = adv1tov2 afppasswd cnid megatron uniconv misc
+
+if USE_APPLETALK
+SUBDIRS += aecho getzones nbp pap psorder
+endif
+
+if HAVE_ATFUNCS
+SUBDIRS += ad
+endif
diff --git a/bin/ad/.gitignore b/bin/ad/.gitignore
new file mode 100644 (file)
index 0000000..28432ec
--- /dev/null
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+ad
+.deps
+.libs
+*.o
diff --git a/bin/ad/Makefile.am b/bin/ad/Makefile.am
new file mode 100644 (file)
index 0000000..7e6fe92
--- /dev/null
@@ -0,0 +1,24 @@
+# Makefile.am for bin/ad/
+
+noinst_HEADERS = ad.h
+
+if USE_BDB
+bin_PROGRAMS = ad
+
+ad_SOURCES = \
+       ad.c \
+       ad_find.c \
+       ad_util.c \
+       ad_ls.c \
+       ad_cp.c \
+       ad_mv.c \
+       ad_rm.c
+
+ad_CFLAGS = -D_PATH_AD=\"$(bindir)/ad\"
+
+ad_LDADD = \
+       $(top_builddir)/libatalk/cnid/libcnid.la \
+       $(top_builddir)/libatalk/libatalk.la \
+       @ACL_LIBS@
+
+endif
diff --git a/bin/ad/ad.c b/bin/ad/ad.c
new file mode 100644 (file)
index 0000000..71a0f74
--- /dev/null
@@ -0,0 +1,66 @@
+/* 
+   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
+   
+   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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+
+#include <atalk/cnid.h>
+#include <atalk/volinfo.h>
+#include <atalk/logger.h>
+#include <atalk/util.h>
+
+#include "ad.h"
+
+static void usage_main(void)
+{
+    printf("Usage: ad ls|cp|rm|mv|find [file|dir, ...]\n");
+}
+
+int main(int argc, char **argv)
+{
+    setuplog("default log_note /dev/tty");
+
+    if (argc < 2) {
+        usage_main();
+        return 1;
+    }
+
+    if (STRCMP(argv[1], ==, "ls"))
+        return ad_ls(argc - 1, argv + 1);
+    else if (STRCMP(argv[1], ==, "cp"))
+        return ad_cp(argc - 1, argv + 1);
+    else if (STRCMP(argv[1], ==, "rm"))
+        return ad_rm(argc - 1, argv + 1);
+    else if (STRCMP(argv[1], ==, "mv"))
+        return ad_mv(argc, argv);
+    else if (STRCMP(argv[1], ==, "find"))
+        return ad_find(argc, argv);
+    else {
+        usage_main();
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/bin/ad/ad.h b/bin/ad/ad.h
new file mode 100644 (file)
index 0000000..9f8fd4b
--- /dev/null
@@ -0,0 +1,81 @@
+/* 
+   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
+   
+   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.
+*/
+
+#ifndef AD_H
+#define AD_H
+
+#define _XOPEN_SOURCE 600
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#include <atalk/ftw.h>
+#include <atalk/volinfo.h>
+#include <atalk/cnid.h>
+
+#define DIR_DOT_OR_DOTDOT(a) \
+        ((strcmp(a, ".") == 0) || (strcmp(a, "..") == 0))
+
+#ifndef TIMESPEC_TO_TIMEVAL
+#define TIMESPEC_TO_TIMEVAL(tv, ts) { \
+    (tv)->tv_sec = (ts)->tv_sec; \
+    (tv)->tv_usec = (ts)->tv_nsec / 1000; \
+    }
+#endif
+
+enum logtype {STD, DBG};
+
+#define SLOG(...)                             \
+    _log(STD, __VA_ARGS__)
+
+#define ERROR(...)                              \
+    do {                                        \
+        _log(STD, __VA_ARGS__);                 \
+        exit(1);                                \
+    } while (0)
+
+typedef struct {
+    struct volinfo volinfo;
+    struct vol     volume;
+    char           db_stamp[ADEDLEN_PRIVSYN];
+} afpvol_t;
+
+extern int log_verbose;             /* Logging flag */
+extern void _log(enum logtype lt, char *fmt, ...);
+
+extern int ad_ls(int argc, char **argv);
+extern int ad_cp(int argc, char **argv);
+extern int ad_rm(int argc, char **argv);
+extern int ad_mv(int argc, char **argv);
+extern int ad_find(int argc, char **argv);
+
+/* ad_util.c */
+extern int openvol(const char *path, afpvol_t *vol);
+extern void closevol(afpvol_t *vol);
+extern cnid_t cnid_for_path(const afpvol_t *vol, const char *path, cnid_t *did);
+extern cnid_t cnid_for_paths_parent(const afpvol_t *vol, const char *path, cnid_t *did);
+extern char *utompath(const struct volinfo *volinfo, const char *upath);
+extern int convert_dots_encoding(const afpvol_t *svol, const afpvol_t *dvol, char *path, size_t buflen);
+
+typedef struct {
+    char *p_end;/* pointer to NULL at end of path */
+    char *target_end;/* pointer to end of target base */
+    char p_path[MAXPATHLEN + 2];/* pointer to the start of a path */
+} PATH_T;
+
+extern PATH_T to;
+extern int fflag, iflag, lflag, nflag, pflag, vflag;
+
+#endif /* AD_H */
diff --git a/bin/ad/ad_cp.c b/bin/ad/ad_cp.c
new file mode 100644 (file)
index 0000000..ac0a737
--- /dev/null
@@ -0,0 +1,1034 @@
+/*
+ * Copyright (c) 2010, Frank Lahm <franklahm@googlemail.com>
+ * Copyright (c) 1988, 1993, 1994
+ * The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * David Hitz of Auspex Systems Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Cp copies source files to target files.
+ *
+ * The global PATH_T structure "to" always contains the path to the
+ * current target file.  Since fts(3) does not change directories,
+ * this path can be either absolute or dot-relative.
+ *
+ * The basic algorithm is to initialize "to" and use fts(3) to traverse
+ * the file hierarchy rooted in the argument list.  A trivial case is the
+ * case of 'cp file1 file2'.  The more interesting case is the case of
+ * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the
+ * path (relative to the root of the traversal) is appended to dir (stored
+ * in "to") to form the final target path.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libgen.h>
+
+#include <atalk/ftw.h>
+#include <atalk/adouble.h>
+#include <atalk/vfs.h>
+#include <atalk/util.h>
+#include <atalk/unix.h>
+#include <atalk/volume.h>
+#include <atalk/volinfo.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
+#include <atalk/queue.h>
+
+#include "ad.h"
+
+#define STRIP_TRAILING_SLASH(p) {                                   \
+        while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/')  \
+            *--(p).p_end = 0;                                       \
+    }
+
+static char emptystring[] = "";
+
+PATH_T to = { to.p_path, emptystring, "" };
+enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
+
+int fflag, iflag, nflag, pflag, vflag;
+mode_t mask;
+
+cnid_t ppdid, pdid, did; /* current dir CNID and parent did*/
+
+static afpvol_t svolume, dvolume;
+static enum op type;
+static int Rflag;
+static volatile sig_atomic_t alarmed;
+static int badcp, rval;
+static int ftw_options = FTW_MOUNT | FTW_PHYS | FTW_ACTIONRETVAL;
+
+static char           *netatalk_dirs[] = {
+    ".AppleDouble",
+    ".AppleDB",
+    ".AppleDesktop",
+    NULL
+};
+
+/* Forward declarations */
+static int copy(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf);
+static int ftw_copy_file(const struct FTW *, const char *, const struct stat *, int);
+static int ftw_copy_link(const struct FTW *, const char *, const struct stat *, int);
+static int setfile(const struct stat *, int);
+static int preserve_dir_acls(const struct stat *, char *, char *);
+static int preserve_fd_acls(int, int);
+
+/*
+  Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
+  Returns pointer to name or NULL.
+*/
+static const char *check_netatalk_dirs(const char *name)
+{
+    int c;
+
+    for (c=0; netatalk_dirs[c]; c++) {
+        if ((strcmp(name, netatalk_dirs[c])) == 0)
+            return netatalk_dirs[c];
+    }
+    return NULL;
+}
+
+static void upfunc(void)
+{
+    did = pdid;
+    pdid = ppdid;
+}
+
+/*
+  SIGNAL handling:
+  catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
+*/
+
+static void sig_handler(int signo)
+{
+    alarmed = 1;
+    return;
+}
+
+static void set_signal(void)
+{
+    struct sigaction sv;
+
+    sv.sa_handler = sig_handler;
+    sv.sa_flags = SA_RESTART;
+    sigemptyset(&sv.sa_mask);
+    if (sigaction(SIGTERM, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGTERM): %s", strerror(errno));
+
+    if (sigaction(SIGINT, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGINT): %s", strerror(errno));
+
+    memset(&sv, 0, sizeof(struct sigaction));
+    sv.sa_handler = SIG_IGN;
+    sigemptyset(&sv.sa_mask);
+
+    if (sigaction(SIGABRT, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGABRT): %s", strerror(errno));
+
+    if (sigaction(SIGHUP, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGHUP): %s", strerror(errno));
+
+    if (sigaction(SIGQUIT, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGQUIT): %s", strerror(errno));
+}
+
+static void usage_cp(void)
+{
+    printf(
+        "Usage: ad cp [-R] [-aipvf] <source_file> <target_file>\n"
+        "       ad cp [-R] [-aipvfx] <source_file [source_file ...]> <target_directory>\n\n"
+        "In the first synopsis form, the cp utility copies the contents of the source_file to the\n"
+        "target_file.  In the second synopsis form, the contents of each named source_file is copied to the\n"
+        "destination target_directory.  The names of the files themselves are not changed.  If cp detects an\n"
+        "attempt to copy a file to itself, the copy will fail.\n\n"
+        "Netatalk AFP volumes are detected by means of their \".AppleDesktop\" directory\n"
+        "which is located in their volume root. When a copy targetting an AFP volume\n"
+        "is detected, its CNID database daemon is connected and all copies will also\n"
+        "go through the CNID database.\n"
+        "AppleDouble files are also copied and created as needed when the target is\n"
+        "an AFP volume.\n\n"
+        "The following options are available:\n\n"
+        "     -a    Archive mode.  Same as -Rp.\n\n"
+        "     -f    For each existing destination pathname, remove it and create a new\n"
+        "           file, without prompting for confirmation regardless of its permis-\n"
+        "           sions.  (The -f option overrides any previous -i or -n options.)\n\n"
+        "     -i    Cause cp to write a prompt to the standard error output before\n"
+        "           copying a file that would overwrite an existing file.  If the\n"
+        "           response from the standard input begins with the character 'y' or\n"
+        "           'Y', the file copy is attempted.  (The -i option overrides any pre-\n"
+        "           vious -f or -n options.)\n\n"
+        "     -n    Do not overwrite an existing file.  (The -n option overrides any\n"
+        "           previous -f or -i options.)\n\n"
+        "     -p    Cause cp to preserve the following attributes of each source file\n"
+        "           in the copy: modification time, access time, file flags, file mode,\n"
+        "           user ID, and group ID, as allowed by permissions.\n"
+        "           If the user ID and group ID cannot be preserved, no error message\n"
+        "           is displayed and the exit value is not altered.\n\n"
+        "     -R    If source_file designates a directory, cp copies the directory and\n"
+        "           the entire subtree connected at that point.If the source_file\n"
+        "           ends in a /, the contents of the directory are copied rather than\n"
+        "           the directory itself.\n\n"
+        "     -v    Cause cp to be verbose, showing files as they are copied.\n\n"
+        "     -x    File system mount points are not traversed.\n\n"
+        );
+    exit(EXIT_FAILURE);
+}
+
+int ad_cp(int argc, char *argv[])
+{
+    struct stat to_stat, tmp_stat;
+    int r, ch, have_trailing_slash;
+    char *target;
+#if 0
+    afpvol_t srcvol;
+    afpvol_t dstvol;
+#endif
+
+    ppdid = pdid = htonl(1);
+    did = htonl(2);
+
+    while ((ch = getopt(argc, argv, "afinpRvx")) != -1)
+        switch (ch) {
+        case 'a':
+            pflag = 1;
+            Rflag = 1;
+            break;
+        case 'f':
+            fflag = 1;
+            iflag = nflag = 0;
+            break;
+        case 'i':
+            iflag = 1;
+            fflag = nflag = 0;
+            break;
+        case 'n':
+            nflag = 1;
+            fflag = iflag = 0;
+            break;
+        case 'p':
+            pflag = 1;
+            break;
+        case 'R':
+            Rflag = 1;
+            break;
+        case 'v':
+            vflag = 1;
+            break;
+        case 'x':
+            ftw_options |= FTW_MOUNT;
+            break;
+        default:
+            usage_cp();
+            break;
+        }
+    argc -= optind;
+    argv += optind;
+
+    if (argc < 2)
+        usage_cp();
+
+    set_signal();
+    cnid_init();
+
+    /* Save the target base in "to". */
+    target = argv[--argc];
+    if ((strlcpy(to.p_path, target, PATH_MAX)) >= PATH_MAX)
+        ERROR("%s: name too long", target);
+
+    to.p_end = to.p_path + strlen(to.p_path);
+    if (to.p_path == to.p_end) {
+        *to.p_end++ = '.';
+        *to.p_end = 0;
+    }
+    have_trailing_slash = (to.p_end[-1] == '/');
+    if (have_trailing_slash)
+        STRIP_TRAILING_SLASH(to);
+    to.target_end = to.p_end;
+
+    /* Set end of argument list */
+    argv[argc] = NULL;
+
+    /*
+     * Cp has two distinct cases:
+     *
+     * cp [-R] source target
+     * cp [-R] source1 ... sourceN directory
+     *
+     * In both cases, source can be either a file or a directory.
+     *
+     * In (1), the target becomes a copy of the source. That is, if the
+     * source is a file, the target will be a file, and likewise for
+     * directories.
+     *
+     * In (2), the real target is not directory, but "directory/source".
+     */
+    r = stat(to.p_path, &to_stat);
+    if (r == -1 && errno != ENOENT)
+        ERROR("%s", to.p_path);
+    if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
+        /*
+         * Case (1).  Target is not a directory.
+         */
+        if (argc > 1)
+            ERROR("%s is not a directory", to.p_path);
+
+        /*
+         * Need to detect the case:
+         *cp -R dir foo
+         * Where dir is a directory and foo does not exist, where
+         * we want pathname concatenations turned on but not for
+         * the initial mkdir().
+         */
+        if (r == -1) {
+            lstat(*argv, &tmp_stat);
+
+            if (S_ISDIR(tmp_stat.st_mode) && Rflag)
+                type = DIR_TO_DNE;
+            else
+                type = FILE_TO_FILE;
+        } else
+            type = FILE_TO_FILE;
+
+        if (have_trailing_slash && type == FILE_TO_FILE) {
+            if (r == -1)
+                ERROR("directory %s does not exist", to.p_path);
+            else
+                ERROR("%s is not a directory", to.p_path);
+        }
+    } else
+        /*
+         * Case (2).  Target is a directory.
+         */
+        type = FILE_TO_DIR;
+
+    /*
+     * Keep an inverted copy of the umask, for use in correcting
+     * permissions on created directories when not using -p.
+     */
+    mask = ~umask(0777);
+    umask(~mask);
+
+#if 0
+    /* Inhereting perms in ad_mkdir etc requires this */
+    ad_setfuid(0);
+#endif
+
+    /* Load .volinfo file for destination*/
+    openvol(to.p_path, &dvolume);
+
+    for (int i = 0; argv[i] != NULL; i++) {
+        /* Load .volinfo file for source */
+        openvol(argv[i], &svolume);
+
+        if (nftw(argv[i], copy, upfunc, 20, ftw_options) == -1) {
+            if (alarmed) {
+                SLOG("...break");
+            } else {
+                SLOG("Error: %s: %s", argv[i], strerror(errno));
+            }
+            closevol(&svolume);
+            closevol(&dvolume);
+        }
+    }
+    return rval;
+}
+
+static int copy(const char *path,
+                const struct stat *statp,
+                int tflag,
+                struct FTW *ftw)
+{
+    static int base = 0;
+
+    struct stat to_stat;
+    int dne;
+    size_t nlen;
+    const char *p;
+    char *target_mid;
+
+    if (alarmed)
+        return -1;
+
+    /* This currently doesn't work with "." */
+    if (strcmp(path, ".") == 0) {
+        ERROR("\".\" not supported");
+    }
+    const char *dir = strrchr(path, '/');
+    if (dir == NULL)
+        dir = path;
+    else
+        dir++;
+    if (check_netatalk_dirs(dir) != NULL)
+        return FTW_SKIP_SUBTREE;
+
+    /*
+     * If we are in case (2) above, we need to append the
+     * source name to the target name.
+     */
+    if (type != FILE_TO_FILE) {
+        /*
+         * Need to remember the roots of traversals to create
+         * correct pathnames.  If there's a directory being
+         * copied to a non-existent directory, e.g.
+         *     cp -R a/dir noexist
+         * the resulting path name should be noexist/foo, not
+         * noexist/dir/foo (where foo is a file in dir), which
+         * is the case where the target exists.
+         *
+         * Also, check for "..".  This is for correct path
+         * concatenation for paths ending in "..", e.g.
+         *     cp -R .. /tmp
+         * Paths ending in ".." are changed to ".".  This is
+         * tricky, but seems the easiest way to fix the problem.
+         *
+         * XXX
+         * Since the first level MUST be FTS_ROOTLEVEL, base
+         * is always initialized.
+         */
+        if (ftw->level == 0) {
+            if (type != DIR_TO_DNE) {
+                base = ftw->base;
+
+                if (strcmp(&path[base], "..") == 0)
+                    base += 1;
+            } else
+                base = strlen(path);
+        }
+
+        p = &path[base];
+        nlen = strlen(path) - base;
+        target_mid = to.target_end;
+        if (*p != '/' && target_mid[-1] != '/')
+            *target_mid++ = '/';
+        *target_mid = 0;
+        if (target_mid - to.p_path + nlen >= PATH_MAX) {
+            SLOG("%s%s: name too long (not copied)", to.p_path, p);
+            badcp = rval = 1;
+            return 0;
+        }
+        (void)strncat(target_mid, p, nlen);
+        to.p_end = target_mid + nlen;
+        *to.p_end = 0;
+        STRIP_TRAILING_SLASH(to);
+    }
+
+    /* Not an error but need to remember it happened */
+    if (stat(to.p_path, &to_stat) == -1)
+        dne = 1;
+    else {
+        if (to_stat.st_dev == statp->st_dev &&
+            to_stat.st_ino == statp->st_ino) {
+            SLOG("%s and %s are identical (not copied).", to.p_path, path);
+            badcp = rval = 1;
+            if (S_ISDIR(statp->st_mode))
+                /* without using glibc extension FTW_ACTIONRETVAL cant handle this */
+                return FTW_SKIP_SUBTREE;
+            return 0;
+        }
+        if (!S_ISDIR(statp->st_mode) &&
+            S_ISDIR(to_stat.st_mode)) {
+            SLOG("cannot overwrite directory %s with "
+                 "non-directory %s",
+                 to.p_path, path);
+            badcp = rval = 1;
+            return 0;
+        }
+        dne = 0;
+    }
+
+    /* Convert basename to appropiate volume encoding */
+    if (dvolume.volinfo.v_path) {
+        if ((convert_dots_encoding(&svolume, &dvolume, to.p_path, MAXPATHLEN)) == -1) {
+            SLOG("Error converting name for %s", to.p_path);
+            badcp = rval = 1;
+            return -1;
+        }
+    }
+
+    switch (statp->st_mode & S_IFMT) {
+    case S_IFLNK:
+        if (ftw_copy_link(ftw, path, statp, !dne))
+            badcp = rval = 1;
+        break;
+    case S_IFDIR:
+        if (!Rflag) {
+            SLOG("%s is a directory", path);
+            badcp = rval = 1;
+            return -1;
+        }
+        /*
+         * If the directory doesn't exist, create the new
+         * one with the from file mode plus owner RWX bits,
+         * modified by the umask.  Trade-off between being
+         * able to write the directory (if from directory is
+         * 555) and not causing a permissions race.  If the
+         * umask blocks owner writes, we fail..
+         */
+        if (dne) {
+            if (mkdir(to.p_path, statp->st_mode | S_IRWXU) < 0)
+                ERROR("mkdir: %s: %s", to.p_path, strerror(errno));
+        } else if (!S_ISDIR(to_stat.st_mode)) {
+            errno = ENOTDIR;
+            ERROR("%s", to.p_path);
+        }
+
+        /* Create ad dir and copy ".Parent" */
+        if (dvolume.volinfo.v_path && dvolume.volinfo.v_adouble == AD_VERSION2) {
+
+            /* Create ".AppleDouble" dir */
+            mode_t omask = umask(0);
+            bstring addir = bfromcstr(to.p_path);
+            bcatcstr(addir, "/.AppleDouble");
+            mkdir(cfrombstr(addir), 02777);
+            bdestroy(addir);
+
+            if (svolume.volinfo.v_path && svolume.volinfo.v_adouble == AD_VERSION2) {
+                /* copy ".Parent" file */
+                if (dvolume.volume.vfs->vfs_copyfile(&dvolume.volume, -1, path, to.p_path)) {
+                    SLOG("Error copying adouble for %s -> %s", path, to.p_path);
+                    badcp = rval = 1;
+                    break;
+                }
+            }
+
+            /* Get CNID of Parent and add new childir to CNID database */
+            ppdid = pdid;
+            if ((did = cnid_for_path(&dvolume, to.p_path, &pdid)) == CNID_INVALID) {
+                SLOG("Error resolving CNID for %s", to.p_path);
+                badcp = rval = 1;
+                return -1;
+            }
+
+            struct adouble ad;
+            struct stat st;
+            if (lstat(to.p_path, &st) != 0) {
+                badcp = rval = 1;
+                break;
+            }
+            ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
+            if (ad_open_metadata(to.p_path, ADFLAGS_DIR, O_RDWR | O_CREAT, &ad) != 0) {
+                ERROR("Error opening adouble for: %s", to.p_path);
+            }
+            ad_setid( &ad, st.st_dev, st.st_ino, did, pdid, dvolume.db_stamp);
+            ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path)));
+            ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
+            ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
+            ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
+            ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
+            ad_flush(&ad);
+            ad_close_metadata(&ad);
+
+            umask(omask);
+        }
+
+        if (pflag) {
+            if (setfile(statp, -1))
+                rval = 1;
+#if 0
+            if (preserve_dir_acls(statp, curr->fts_accpath, to.p_path) != 0)
+                rval = 1;
+#endif
+        }
+        break;
+
+    case S_IFBLK:
+    case S_IFCHR:
+        SLOG("%s is a device file (not copied).", path);
+        break;
+    case S_IFSOCK:
+        SLOG("%s is a socket (not copied).", path);
+        break;
+    case S_IFIFO:
+        SLOG("%s is a FIFO (not copied).", path);
+        break;
+    default:
+        if (ftw_copy_file(ftw, path, statp, dne))
+            badcp = rval = 1;
+
+        if (dvolume.volinfo.v_path && dvolume.volinfo.v_adouble == AD_VERSION2) {
+
+            mode_t omask = umask(0);
+            if (svolume.volinfo.v_path && svolume.volinfo.v_adouble == AD_VERSION2) {
+                /* copy ad-file */
+                if (dvolume.volume.vfs->vfs_copyfile(&dvolume.volume, -1, path, to.p_path)) {
+                    SLOG("Error copying adouble for %s -> %s", path, to.p_path);
+                    badcp = rval = 1;
+                    break;
+                }
+            }
+
+            /* Get CNID of Parent and add new childir to CNID database */
+            pdid = did;
+            cnid_t cnid;
+            if ((cnid = cnid_for_path(&dvolume, to.p_path, &did)) == CNID_INVALID) {
+                SLOG("Error resolving CNID for %s", to.p_path);
+                badcp = rval = 1;
+                return -1;
+            }
+
+            struct adouble ad;
+            struct stat st;
+            if (lstat(to.p_path, &st) != 0) {
+                badcp = rval = 1;
+                break;
+            }
+            ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
+            if (ad_open_metadata(to.p_path, 0, O_RDWR | O_CREAT, &ad) != 0) {
+                ERROR("Error opening adouble for: %s", to.p_path);
+            }
+            ad_setid( &ad, st.st_dev, st.st_ino, cnid, did, dvolume.db_stamp);
+            ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path)));
+            ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime);
+            ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime);
+            ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime);
+            ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START);
+            ad_flush(&ad);
+            ad_close_metadata(&ad);
+            umask(omask);
+        }
+        break;
+    }
+    if (vflag && !badcp)
+        (void)printf("%s -> %s\n", path, to.p_path);
+
+    return 0;
+}
+
+/* Memory strategy threshold, in pages: if physmem is larger then this, use a large buffer */
+#define PHYSPAGES_THRESHOLD (32*1024)
+
+/* Maximum buffer size in bytes - do not allow it to grow larger than this */
+#define BUFSIZE_MAX (2*1024*1024)
+
+/* Small (default) buffer size in bytes. It's inefficient for this to be smaller than MAXPHYS */
+#define MAXPHYS (64 * 1024)
+#define BUFSIZE_SMALL (MAXPHYS)
+
+static int ftw_copy_file(const struct FTW *entp,
+                         const char *spath,
+                         const struct stat *sp,
+                         int dne)
+{
+    static char *buf = NULL;
+    static size_t bufsize;
+    ssize_t wcount;
+    size_t wresid;
+    off_t wtotal;
+    int ch, checkch, from_fd = 0, rcount, rval, to_fd = 0;
+    char *bufp;
+    char *p;
+
+    if ((from_fd = open(spath, O_RDONLY, 0)) == -1) {
+        SLOG("%s: %s", spath, strerror(errno));
+        return (1);
+    }
+
+    /*
+     * If the file exists and we're interactive, verify with the user.
+     * If the file DNE, set the mode to be the from file, minus setuid
+     * bits, modified by the umask; arguably wrong, but it makes copying
+     * executables work right and it's been that way forever.  (The
+     * other choice is 666 or'ed with the execute bits on the from file
+     * modified by the umask.)
+     */
+    if (!dne) {
+#define YESNO "(y/n [n]) "
+        if (nflag) {
+            if (vflag)
+                printf("%s not overwritten\n", to.p_path);
+            (void)close(from_fd);
+            return (0);
+        } else if (iflag) {
+            (void)fprintf(stderr, "overwrite %s? %s",
+                          to.p_path, YESNO);
+            checkch = ch = getchar();
+            while (ch != '\n' && ch != EOF)
+                ch = getchar();
+            if (checkch != 'y' && checkch != 'Y') {
+                (void)close(from_fd);
+                (void)fprintf(stderr, "not overwritten\n");
+                return (1);
+            }
+        }
+
+        if (fflag) {
+            /* remove existing destination file name,
+             * create a new file  */
+            (void)unlink(to.p_path);
+            (void)dvolume.volume.vfs->vfs_deletefile(&dvolume.volume, -1, to.p_path);
+            to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
+                         sp->st_mode & ~(S_ISUID | S_ISGID));
+        } else {
+            /* overwrite existing destination file name */
+            to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
+        }
+    } else {
+        to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
+                     sp->st_mode & ~(S_ISUID | S_ISGID));
+    }
+
+    if (to_fd == -1) {
+        SLOG("%s: %s", to.p_path, strerror(errno));
+        (void)close(from_fd);
+        return (1);
+    }
+
+    rval = 0;
+
+    /*
+     * Mmap and write if less than 8M (the limit is so we don't totally
+     * trash memory on big files.  This is really a minor hack, but it
+     * wins some CPU back.
+     * Some filesystems, such as smbnetfs, don't support mmap,
+     * so this is a best-effort attempt.
+     */
+
+    if (S_ISREG(sp->st_mode) && sp->st_size > 0 &&
+        sp->st_size <= 8 * 1024 * 1024 &&
+        (p = mmap(NULL, (size_t)sp->st_size, PROT_READ,
+                  MAP_SHARED, from_fd, (off_t)0)) != MAP_FAILED) {
+        wtotal = 0;
+        for (bufp = p, wresid = sp->st_size; ;
+             bufp += wcount, wresid -= (size_t)wcount) {
+            wcount = write(to_fd, bufp, wresid);
+            if (wcount <= 0)
+                break;
+            wtotal += wcount;
+            if (wcount >= (ssize_t)wresid)
+                break;
+        }
+        if (wcount != (ssize_t)wresid) {
+            SLOG("%s: %s", to.p_path, strerror(errno));
+            rval = 1;
+        }
+        /* Some systems don't unmap on close(2). */
+        if (munmap(p, sp->st_size) < 0) {
+            SLOG("%s: %s", spath, strerror(errno));
+            rval = 1;
+        }
+    } else {
+        if (buf == NULL) {
+            /*
+             * Note that buf and bufsize are static. If
+             * malloc() fails, it will fail at the start
+             * and not copy only some files.
+             */
+            if (sysconf(_SC_PHYS_PAGES) >
+                PHYSPAGES_THRESHOLD)
+                bufsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
+            else
+                bufsize = BUFSIZE_SMALL;
+            buf = malloc(bufsize);
+            if (buf == NULL)
+                ERROR("Not enough memory");
+
+        }
+        wtotal = 0;
+        while ((rcount = read(from_fd, buf, bufsize)) > 0) {
+            for (bufp = buf, wresid = rcount; ;
+                 bufp += wcount, wresid -= wcount) {
+                wcount = write(to_fd, bufp, wresid);
+                if (wcount <= 0)
+                    break;
+                wtotal += wcount;
+                if (wcount >= (ssize_t)wresid)
+                    break;
+            }
+            if (wcount != (ssize_t)wresid) {
+                SLOG("%s: %s", to.p_path, strerror(errno));
+                rval = 1;
+                break;
+            }
+        }
+        if (rcount < 0) {
+            SLOG("%s: %s", spath, strerror(errno));
+            rval = 1;
+        }
+    }
+
+    /*
+     * Don't remove the target even after an error.  The target might
+     * not be a regular file, or its attributes might be important,
+     * or its contents might be irreplaceable.  It would only be safe
+     * to remove it if we created it and its length is 0.
+     */
+
+    if (pflag && setfile(sp, to_fd))
+        rval = 1;
+    if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
+        rval = 1;
+    if (close(to_fd)) {
+        SLOG("%s: %s", to.p_path, strerror(errno));
+        rval = 1;
+    }
+
+    (void)close(from_fd);
+
+    return (rval);
+}
+
+static int ftw_copy_link(const struct FTW *p,
+                         const char *spath,
+                         const struct stat *sstp,
+                         int exists)
+{
+    int len;
+    char llink[PATH_MAX];
+
+    if ((len = readlink(spath, llink, sizeof(llink) - 1)) == -1) {
+        SLOG("readlink: %s: %s", spath, strerror(errno));
+        return (1);
+    }
+    llink[len] = '\0';
+    if (exists && unlink(to.p_path)) {
+        SLOG("unlink: %s: %s", to.p_path, strerror(errno));
+        return (1);
+    }
+    if (symlink(llink, to.p_path)) {
+        SLOG("symlink: %s: %s", llink, strerror(errno));
+        return (1);
+    }
+    return (pflag ? setfile(sstp, -1) : 0);
+}
+
+static int setfile(const struct stat *fs, int fd)
+{
+    static struct timeval tv[2];
+    struct stat ts;
+    int rval, gotstat, islink, fdval;
+    mode_t mode;
+
+    rval = 0;
+    fdval = fd != -1;
+    islink = !fdval && S_ISLNK(fs->st_mode);
+    mode = fs->st_mode & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO);
+
+    TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atim);
+    TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtim);
+    if (utimes(to.p_path, tv)) {
+        SLOG("utimes: %s", to.p_path);
+        rval = 1;
+    }
+    if (fdval ? fstat(fd, &ts) :
+        (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts)))
+        gotstat = 0;
+    else {
+        gotstat = 1;
+        ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
+            S_IRWXU | S_IRWXG | S_IRWXO;
+    }
+    /*
+     * Changing the ownership probably won't succeed, unless we're root
+     * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
+     * the mode; current BSD behavior is to remove all setuid bits on
+     * chown.  If chown fails, lose setuid/setgid bits.
+     */
+    if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
+        if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) :
+            (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) :
+             chown(to.p_path, fs->st_uid, fs->st_gid))) {
+            if (errno != EPERM) {
+                SLOG("chown: %s: %s", to.p_path, strerror(errno));
+                rval = 1;
+            }
+            mode &= ~(S_ISUID | S_ISGID);
+        }
+
+    if (!gotstat || mode != ts.st_mode)
+        if (fdval ? fchmod(fd, mode) : chmod(to.p_path, mode)) {
+            SLOG("chmod: %s: %s", to.p_path, strerror(errno));
+            rval = 1;
+        }
+
+#ifdef HAVE_ST_FLAGS
+    if (!gotstat || fs->st_flags != ts.st_flags)
+        if (fdval ?
+            fchflags(fd, fs->st_flags) :
+            (islink ? lchflags(to.p_path, fs->st_flags) :
+             chflags(to.p_path, fs->st_flags))) {
+            SLOG("chflags: %s: %s", to.p_path, strerror(errno));
+            rval = 1;
+        }
+#endif
+
+    return (rval);
+}
+
+static int preserve_fd_acls(int source_fd, int dest_fd)
+{
+#if 0
+    acl_t acl;
+    acl_type_t acl_type;
+    int acl_supported = 0, ret, trivial;
+
+    ret = fpathconf(source_fd, _PC_ACL_NFS4);
+    if (ret > 0 ) {
+        acl_supported = 1;
+        acl_type = ACL_TYPE_NFS4;
+    } else if (ret < 0 && errno != EINVAL) {
+        warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", to.p_path);
+        return (1);
+    }
+    if (acl_supported == 0) {
+        ret = fpathconf(source_fd, _PC_ACL_EXTENDED);
+        if (ret > 0 ) {
+            acl_supported = 1;
+            acl_type = ACL_TYPE_ACCESS;
+        } else if (ret < 0 && errno != EINVAL) {
+            warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
+                 to.p_path);
+            return (1);
+        }
+    }
+    if (acl_supported == 0)
+        return (0);
+
+    acl = acl_get_fd_np(source_fd, acl_type);
+    if (acl == NULL) {
+        warn("failed to get acl entries while setting %s", to.p_path);
+        return (1);
+    }
+    if (acl_is_trivial_np(acl, &trivial)) {
+        warn("acl_is_trivial() failed for %s", to.p_path);
+        acl_free(acl);
+        return (1);
+    }
+    if (trivial) {
+        acl_free(acl);
+        return (0);
+    }
+    if (acl_set_fd_np(dest_fd, acl, acl_type) < 0) {
+        warn("failed to set acl entries for %s", to.p_path);
+        acl_free(acl);
+        return (1);
+    }
+    acl_free(acl);
+#endif
+    return (0);
+}
+
+static int preserve_dir_acls(const struct stat *fs, char *source_dir, char *dest_dir)
+{
+#if 0
+    acl_t (*aclgetf)(const char *, acl_type_t);
+    int (*aclsetf)(const char *, acl_type_t, acl_t);
+    struct acl *aclp;
+    acl_t acl;
+    acl_type_t acl_type;
+    int acl_supported = 0, ret, trivial;
+
+    ret = pathconf(source_dir, _PC_ACL_NFS4);
+    if (ret > 0) {
+        acl_supported = 1;
+        acl_type = ACL_TYPE_NFS4;
+    } else if (ret < 0 && errno != EINVAL) {
+        warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", source_dir);
+        return (1);
+    }
+    if (acl_supported == 0) {
+        ret = pathconf(source_dir, _PC_ACL_EXTENDED);
+        if (ret > 0) {
+            acl_supported = 1;
+            acl_type = ACL_TYPE_ACCESS;
+        } else if (ret < 0 && errno != EINVAL) {
+            warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
+                 source_dir);
+            return (1);
+        }
+    }
+    if (acl_supported == 0)
+        return (0);
+
+    /*
+     * If the file is a link we will not follow it
+     */
+    if (S_ISLNK(fs->st_mode)) {
+        aclgetf = acl_get_link_np;
+        aclsetf = acl_set_link_np;
+    } else {
+        aclgetf = acl_get_file;
+        aclsetf = acl_set_file;
+    }
+    if (acl_type == ACL_TYPE_ACCESS) {
+        /*
+         * Even if there is no ACL_TYPE_DEFAULT entry here, a zero
+         * size ACL will be returned. So it is not safe to simply
+         * check the pointer to see if the default ACL is present.
+         */
+        acl = aclgetf(source_dir, ACL_TYPE_DEFAULT);
+        if (acl == NULL) {
+            warn("failed to get default acl entries on %s",
+                 source_dir);
+            return (1);
+        }
+        aclp = &acl->ats_acl;
+        if (aclp->acl_cnt != 0 && aclsetf(dest_dir,
+                                          ACL_TYPE_DEFAULT, acl) < 0) {
+            warn("failed to set default acl entries on %s",
+                 dest_dir);
+            acl_free(acl);
+            return (1);
+        }
+        acl_free(acl);
+    }
+    acl = aclgetf(source_dir, acl_type);
+    if (acl == NULL) {
+        warn("failed to get acl entries on %s", source_dir);
+        return (1);
+    }
+    if (acl_is_trivial_np(acl, &trivial)) {
+        warn("acl_is_trivial() failed on %s", source_dir);
+        acl_free(acl);
+        return (1);
+    }
+    if (trivial) {
+        acl_free(acl);
+        return (0);
+    }
+    if (aclsetf(dest_dir, acl_type, acl) < 0) {
+        warn("failed to set acl entries on %s", dest_dir);
+        acl_free(acl);
+        return (1);
+    }
+    acl_free(acl);
+#endif
+    return (0);
+}
diff --git a/bin/ad/ad_find.c b/bin/ad/ad_find.c
new file mode 100644 (file)
index 0000000..8501500
--- /dev/null
@@ -0,0 +1,177 @@
+/* 
+   Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+   
+   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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+#include <atalk/cnid_dbd_private.h>
+#include <atalk/volinfo.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
+#include <atalk/directory.h>
+#include <atalk/util.h>
+#include <atalk/unicode.h>
+#include "ad.h"
+
+static volatile sig_atomic_t alarmed;
+
+/*
+  SIGNAL handling:
+  catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
+*/
+
+static void sig_handler(int signo)
+{
+    alarmed = 1;
+    return;
+}
+
+static void set_signal(void)
+{
+    struct sigaction sv;
+
+    sv.sa_handler = sig_handler;
+    sv.sa_flags = SA_RESTART;
+    sigemptyset(&sv.sa_mask);
+    if (sigaction(SIGTERM, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGTERM): %s", strerror(errno));
+
+    if (sigaction(SIGINT, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGINT): %s", strerror(errno));
+
+    memset(&sv, 0, sizeof(struct sigaction));
+    sv.sa_handler = SIG_IGN;
+    sigemptyset(&sv.sa_mask);
+
+    if (sigaction(SIGABRT, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGABRT): %s", strerror(errno));
+
+    if (sigaction(SIGHUP, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGHUP): %s", strerror(errno));
+
+    if (sigaction(SIGQUIT, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGQUIT): %s", strerror(errno));
+}
+
+static void usage_find(void)
+{
+    printf(
+        "Usage: ad find [-v VOLUME_PATH] NAME\n"
+        );
+}
+
+int ad_find(int argc, char **argv)
+{
+    int c, ret;
+    afpvol_t vol;
+    const char *srchvol = getcwdpath();
+
+    while ((c = getopt(argc-1, &argv[1], ":v:")) != -1) {
+        switch(c) {
+        case 'v':
+            srchvol = strdup(optarg);
+            break;
+        case ':':
+        case '?':
+            usage_find();
+            return -1;
+            break;
+        }
+
+    }
+    optind++;
+
+    if ((argc - optind) != 1) {
+        usage_find();
+        exit(1);
+    }
+
+    set_signal();
+    cnid_init();
+
+    if (openvol(srchvol, &vol) != 0)
+        ERROR("Cant open volume \"%s\"", srchvol);
+
+    uint16_t flags = CONV_TOLOWER;
+    char namebuf[MAXPATHLEN + 1];
+    if (convert_charset(vol.volinfo.v_volcharset,
+                        vol.volinfo.v_volcharset,
+                        vol.volinfo.v_maccharset,
+                        argv[optind],
+                        strlen(argv[optind]),
+                        namebuf,
+                        MAXPATHLEN,
+                        &flags) == (size_t)-1) {
+        ERROR("conversion error");
+    }
+
+    int count;
+    char resbuf[DBD_MAX_SRCH_RSLTS * sizeof(cnid_t)];
+    if ((count = cnid_find(vol.volume.v_cdb,
+                           namebuf,
+                           strlen(namebuf),
+                           resbuf,
+                           sizeof(resbuf))) < 1) {
+        ret = 1;
+    } else {
+        ret = 0;
+        cnid_t cnid;
+        char *bufp = resbuf;
+        bstring sep = bfromcstr("/");
+        while (count--) {
+            memcpy(&cnid, bufp, sizeof(cnid_t));
+            bufp += sizeof(cnid_t);
+
+            bstring path = NULL;
+            bstring volpath = bfromcstr(vol.volinfo.v_path);
+            BSTRING_STRIP_SLASH(volpath);
+            char buffer[12 + MAXPATHLEN + 1];
+            int buflen = 12 + MAXPATHLEN + 1;
+            char *name;
+            cnid_t did = cnid;
+            struct bstrList *pathlist = bstrListCreateMin(32);
+
+            while (did != DIRDID_ROOT) {
+                if ((name = cnid_resolve(vol.volume.v_cdb, &did, buffer, buflen)) == NULL)
+                    goto next;
+                bstrListPush(pathlist, bfromcstr(name));
+            }
+            bstrListPush(pathlist, volpath);
+            path = bjoinInv(pathlist, sep);
+            
+            printf("%s\n", cfrombstr(path));
+
+        next:
+            bstrListDestroy(pathlist);
+            bdestroy(path);
+        }
+        bdestroy(sep);
+    }
+
+    closevol(&vol);
+
+    return ret;
+}
diff --git a/bin/ad/ad_ls.c b/bin/ad/ad_ls.c
new file mode 100644 (file)
index 0000000..63226a3
--- /dev/null
@@ -0,0 +1,688 @@
+/* 
+   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
+   
+   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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <grp.h>
+#include <time.h>
+
+#include <atalk/adouble.h>
+#include <atalk/cnid.h>
+#include <atalk/volinfo.h>
+#include "ad.h"
+
+#define ADv2_DIRNAME ".AppleDouble"
+
+#define DIR_DOT_OR_DOTDOT(a) \
+        ((strcmp(a, ".") == 0) || (strcmp(a, "..") == 0))
+
+static volatile sig_atomic_t alarmed;
+
+/* ls options */
+static int ls_a;
+static int ls_l;
+static int ls_R;
+static int ls_d;
+static int ls_u;
+
+/* Used for pretty printing */
+static int first = 1;
+static int recursion;
+
+static char           *netatalk_dirs[] = {
+    ADv2_DIRNAME,
+    ".AppleDB",
+    ".AppleDesktop",
+    NULL
+};
+
+static char *labels[] = {
+    "---",
+    "gry",
+    "gre",
+    "vio",
+    "blu",
+    "yel",
+    "red",
+    "ora"
+};
+
+/*
+  SIGNAL handling:
+  catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
+*/
+
+static void sig_handler(int signo)
+{
+    alarmed = 1;
+    return;
+}
+
+static void set_signal(void)
+{
+    struct sigaction sv;
+
+    sv.sa_handler = sig_handler;
+    sv.sa_flags = SA_RESTART;
+    sigemptyset(&sv.sa_mask);
+    if (sigaction(SIGTERM, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGTERM): %s", strerror(errno));
+
+    if (sigaction(SIGINT, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGINT): %s", strerror(errno));
+
+    memset(&sv, 0, sizeof(struct sigaction));
+    sv.sa_handler = SIG_IGN;
+    sigemptyset(&sv.sa_mask);
+
+    if (sigaction(SIGABRT, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGABRT): %s", strerror(errno));
+
+    if (sigaction(SIGHUP, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGHUP): %s", strerror(errno));
+
+    if (sigaction(SIGQUIT, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGQUIT): %s", strerror(errno));
+}
+
+/*
+  Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
+  Returns pointer to name or NULL.
+*/
+static const char *check_netatalk_dirs(const char *name)
+{
+    int c;
+
+    for (c=0; netatalk_dirs[c]; c++) {
+        if ((strcmp(name, netatalk_dirs[c])) == 0)
+            return netatalk_dirs[c];
+    }
+    return NULL;
+}
+
+
+static void usage_ls(void)
+{
+    printf(
+        "Usage: ad ls [-dRl[u]] [file|dir, ...]\n\n"
+        "  -l Long Output [-u: unix info]:\n"
+        "     <unixinfo ...> <FinderFlags> <AFPAttributes> <Color> <Type> <Creator> <CNID from AppleDouble> <name>\n\n"
+        "     FinderFlags (valid for (f)ile and/or (d)irectory):\n"
+        "       d = On Desktop (f/d)\n"
+        "       e = Hidden extension (f/d)\n"
+        "       m = Shared (can run multiple times) (f)\n"
+        "       n = No INIT resources (f)\n"
+        "       i = Inited (f/d)\n"
+        "       c = Custom icon (f/d)\n"
+        "       t = Stationery (f)\n"
+        "       s = Name locked (f/d)\n"
+        "       b = Bundle (f/d)\n"
+        "       v = Invisible (f/d)\n"
+        "       a = Alias file (f/d)\n\n"
+        "     AFPAttributes:\n"
+        "       y = System (f/d)\n"
+        "       w = No write (f)\n"
+        "       p = Needs backup (f/d)\n"
+        "       r = No rename (f/d)\n"
+        "       l = No delete (f/d)\n"
+        "       o = No copy (f)\n\n"
+        "     Note: any letter appearing in uppercase means the flag is set\n"
+        "           but it's a directory for which the flag is not allowed.\n"
+        );
+}
+
+static void print_numlinks(const struct stat *statp)
+{
+    printf("%5ld", (long)statp->st_nlink);
+}
+
+static void print_owner(const struct stat *statp)
+{
+    struct passwd *pwd = getpwuid(statp->st_uid);
+
+    if (pwd == NULL)
+        printf(" %-8ld", (long)statp->st_uid);
+    else
+        printf(" %-8s", pwd->pw_name);
+}
+
+static void print_group(const struct stat *statp)
+{
+    struct group *grp = getgrgid(statp->st_gid);
+
+    if (grp == NULL)
+        printf(" %-8ld", (long)statp->st_gid);
+    else
+        printf(" %-8s", grp->gr_name);
+}
+
+static void print_size(const struct stat *statp)
+{
+    switch (statp->st_mode & S_IFMT) {
+    case S_IFCHR:
+    case S_IFBLK:
+        printf("%4u,%4u", (unsigned)(statp->st_rdev >> 8),
+               (unsigned)(statp->st_rdev & 0xFF));
+        break;
+    default:
+        printf("%9lu", (unsigned long)statp->st_size);
+    }
+}
+
+static void print_date(const struct stat *statp)
+{
+    time_t now;
+    double diff;
+    char buf[100], *fmt;
+
+    if (time(&now) == -1) {
+        printf(" ????????????");
+        return;
+    }
+    diff = difftime(now, statp->st_mtime);
+    if (diff < 0 || diff > 60 * 60 * 24 * 182.5)
+        fmt = "%b %e  %Y";
+    else
+        fmt = "%b %e %H:%M";
+    strftime(buf, sizeof(buf), fmt, localtime(&statp->st_mtime));
+    printf(" %s", buf);
+}
+
+static void print_flags(char *path, afpvol_t *vol, const struct stat *st)
+{
+    int adflags = 0;
+    struct adouble ad;
+    char *FinderInfo;
+    uint16_t FinderFlags;
+    uint16_t AFPattributes;
+    char type[5] = "----";
+    char creator[5] = "----";
+    int i;
+    uint32_t cnid;
+
+    if (S_ISDIR(st->st_mode))
+        adflags = ADFLAGS_DIR;
+
+    if (vol->volinfo.v_path == NULL)
+        return;
+
+    ad_init(&ad, vol->volinfo.v_adouble, vol->volinfo.v_ad_options);
+
+    if ( ad_metadata(path, adflags, &ad) < 0 )
+        return;
+
+    FinderInfo = ad_entry(&ad, ADEID_FINDERI);
+
+    memcpy(&FinderFlags, FinderInfo + 8, 2);
+    FinderFlags = ntohs(FinderFlags);
+
+    memcpy(type, FinderInfo, 4);
+    memcpy(creator, FinderInfo + 4, 4);
+
+    ad_getattr(&ad, &AFPattributes);
+    AFPattributes = ntohs(AFPattributes);
+
+    /*
+      Finder flags. Lowercase means valid, uppercase means invalid because
+      object is a dir and flag is only valid for files.
+    */
+    putchar(' ');
+    if (FinderFlags & FINDERINFO_ISONDESK)
+        putchar('d');
+    else
+        putchar('-');
+
+    if (FinderFlags & FINDERINFO_HIDEEXT)
+        putchar('e');
+    else
+        putchar('-');
+
+    if (FinderFlags & FINDERINFO_ISHARED) {
+        if (adflags & ADFLAGS_DIR)
+            putchar('M');
+        else
+            putchar('m');
+    } else
+        putchar('-');
+
+    if (FinderFlags & FINDERINFO_HASNOINITS) {
+        if (adflags & ADFLAGS_DIR)
+            putchar('N');
+        else
+            putchar('n');
+    } else
+        putchar('-');
+
+    if (FinderFlags & FINDERINFO_HASBEENINITED)
+        putchar('i');
+    else
+        putchar('-');
+
+    if (FinderFlags & FINDERINFO_HASCUSTOMICON)
+        putchar('c');
+    else
+        putchar('-');
+
+    if (FinderFlags & FINDERINFO_ISSTATIONNERY) {
+        if (adflags & ADFLAGS_DIR)
+            putchar('T');
+        else
+            putchar('t');
+    } else
+        putchar('-');
+
+    if (FinderFlags & FINDERINFO_NAMELOCKED)
+        putchar('s');
+    else
+        putchar('-');
+
+    if (FinderFlags & FINDERINFO_HASBUNDLE)
+        putchar('b');
+    else
+        putchar('-');
+
+    if (FinderFlags & FINDERINFO_INVISIBLE)
+        putchar('v');
+    else
+        putchar('-');
+
+    if (FinderFlags & FINDERINFO_ISALIAS)
+        putchar('a');
+    else
+        putchar('-');
+
+    putchar(' ');
+
+    /* AFP attributes */
+    if (AFPattributes & ATTRBIT_SYSTEM)
+        putchar('y');
+    else
+        putchar('-');
+
+    if (AFPattributes & ATTRBIT_NOWRITE) {
+        if (adflags & ADFLAGS_DIR)
+            putchar('W');
+        else
+            putchar('w');
+    } else
+        putchar('-');
+
+    if (AFPattributes & ATTRBIT_BACKUP)
+        putchar('p');
+    else
+        putchar('-');
+
+    if (AFPattributes & ATTRBIT_NORENAME)
+        putchar('r');
+    else
+        putchar('-');
+
+    if (AFPattributes & ATTRBIT_NODELETE)
+        putchar('l');
+    else
+        putchar('-');
+
+    if (AFPattributes & ATTRBIT_NOCOPY) {
+        if (adflags & ADFLAGS_DIR)
+            putchar('O');
+        else
+            putchar('o');                
+    } else
+        putchar('-');
+
+    /* Color */
+    printf(" %s ", labels[(FinderFlags & FINDERINFO_COLOR) >> 1]);
+
+    /* Type & Creator */
+    for(i=0; i<4; i++) {
+        if (isalnum(type[i]))
+            putchar(type[i]);
+        else
+            putchar('-');
+    }
+    putchar(' '); 
+    for(i=0; i<4; i++) {
+        if (isalnum(creator[i]))
+            putchar(creator[i]);
+        else
+            putchar('-');
+    }
+    putchar(' '); 
+
+    /* CNID */
+    cnid = ad_forcegetid(&ad);
+    if (cnid)
+        printf(" %10u ", ntohl(cnid));
+    else
+        printf(" !ADVOL_CACHE ");
+
+    ad_close_metadata(&ad);
+}
+
+#define TYPE(b) ((st->st_mode & (S_IFMT)) == (b))
+#define MODE(b) ((st->st_mode & (b)) == (b))
+
+static void print_mode(const struct stat *st)
+{
+    if (TYPE(S_IFBLK))
+        putchar('b');
+    else if (TYPE(S_IFCHR))
+        putchar('c');
+    else if (TYPE(S_IFDIR))
+        putchar('d');
+    else if (TYPE(S_IFIFO))
+        putchar('p');
+    else if (TYPE(S_IFREG))
+        putchar('-');
+    else if (TYPE(S_IFLNK))
+        putchar('l');
+    else if (TYPE(S_IFSOCK))
+        putchar('s');
+    else
+        putchar('?');
+    putchar(MODE(S_IRUSR) ? 'r' : '-');
+    putchar(MODE(S_IWUSR) ? 'w' : '-');
+    if (MODE(S_ISUID)) {
+        if (MODE(S_IXUSR))
+            putchar('s');
+        else
+            putchar('S');
+    }
+    else if (MODE(S_IXUSR))
+        putchar('x');
+    else
+        putchar('-');
+    putchar(MODE(S_IRGRP) ? 'r' : '-');
+    putchar(MODE(S_IWGRP) ? 'w' : '-');
+    if (MODE(S_ISGID)) {
+        if (MODE(S_IXGRP))
+            putchar('s');
+        else
+            putchar('S');
+    }
+    else if (MODE(S_IXGRP))
+        putchar('x');
+    else
+        putchar('-');
+    putchar(MODE(S_IROTH) ? 'r' : '-');
+    putchar(MODE(S_IWOTH) ? 'w' : '-');
+    if (MODE(S_IFDIR) && MODE(S_ISVTX)) {
+        if (MODE(S_IXOTH))
+            putchar('t');
+        else
+            putchar('T');
+    }
+    else if (MODE(S_IXOTH))
+        putchar('x');
+    else
+        putchar('-');
+}
+#undef TYPE
+#undef MODE
+
+static int ad_print(char *path, const struct stat *st, afpvol_t *vol)
+{
+    if ( ! ls_l) {
+        printf("%s  ", path);
+        if (ls_d)
+            printf("\n");
+        return 0;
+    }
+
+    /* Long output */
+    if (ls_u) {
+        print_mode(st);
+        print_numlinks(st);
+        print_owner(st);
+        print_group(st);
+        print_size(st);
+        print_date(st);
+    }
+    print_flags(path, vol, st);
+    printf("  %s\n", path);    
+
+
+    return 0;
+}
+
+static int ad_ls_r(char *path, afpvol_t *vol)
+{
+    int ret = 0, cwd, dirprinted = 0, dirempty;
+    const char *name;
+    char *tmp;
+    static char cwdpath[MAXPATHLEN+1];
+    DIR *dp;
+    struct dirent *ep;
+    static struct stat st;      /* Save some stack space */
+
+    if ( first)
+        cwdpath[0] = 0;
+    else
+        strcat(cwdpath, "/");
+
+    strcat(cwdpath, path);
+    first = 0;
+
+    if (lstat(path, &st) < 0) {
+        perror("Can't stat");
+        return -1;
+    }
+    /* If its a file or a dir with -d option call ad_print and return */
+    if (S_ISREG(st.st_mode) || ls_d)
+        return ad_print(path, &st, vol);
+
+    /* Its a dir: chdir to it remembering where we started */
+    if ((cwd = open(".", O_RDONLY)) == -1) {
+        perror("Cant open .");
+        return -1;
+    }
+    if (chdir(path) != 0) {
+        perror("Cant chdir");
+        close(cwd);
+        return -1;
+    }
+
+    if ((dp = opendir (".")) == NULL) {
+        perror("Couldn't opendir .");
+        return -1;
+    }
+
+    /* First run: print everything */
+    dirempty = 1;
+    while ((ep = readdir (dp))) {
+        if (alarmed) {
+            ret = -1;
+            goto exit;
+        }
+
+        /* Check if its "." or ".." */
+        if (DIR_DOT_OR_DOTDOT(ep->d_name))
+            continue;
+
+        /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
+        if ((name = check_netatalk_dirs(ep->d_name)) != NULL)
+            continue;
+
+        if ((ep->d_name[0] == '.') && ! ls_a)
+            continue;
+
+        dirempty = 0;
+
+        if (recursion && ! dirprinted) {
+            printf("\n%s:\n", cwdpath);
+            dirprinted = 1;
+        }
+
+        if (lstat(ep->d_name, &st) < 0) {
+            perror("Can't stat");
+            return -1;
+        }
+
+        ret = ad_print(ep->d_name, &st, vol);
+        if (ret != 0)
+            goto exit;
+    }
+
+    if (! ls_l && ! dirempty)
+        printf("\n");
+
+    /* Second run: recurse to dirs */
+    if (ls_R) {
+        rewinddir(dp);
+        while ((ep = readdir (dp))) {
+            if (alarmed) {
+                ret = -1;
+                goto exit;
+            }
+
+            /* Check if its "." or ".." */
+            if (DIR_DOT_OR_DOTDOT(ep->d_name))
+                continue;
+
+            /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
+            if ((name = check_netatalk_dirs(ep->d_name)) != NULL)
+                continue;
+
+            if ((ret = lstat(ep->d_name, &st)) < 0) {
+                perror("Can't stat");
+                goto exit;
+            }
+
+            /* Recursion */
+            if (S_ISDIR(st.st_mode)) {
+                recursion = 1;
+                ret = ad_ls_r(ep->d_name, vol);
+            }
+            if (ret != 0)
+                goto exit;
+        }
+    }
+
+exit:
+    closedir(dp);
+    fchdir(cwd);
+    close(cwd);
+
+    tmp = strrchr(cwdpath, '/');
+    if (tmp)
+        *tmp = 0;
+
+    return ret;
+}
+
+int ad_ls(int argc, char **argv)
+{
+    int c, firstarg;
+    afpvol_t vol;
+    struct stat st;
+
+    while ((c = getopt(argc, argv, ":adlRu")) != -1) {
+        switch(c) {
+        case 'a':
+            ls_a = 1;
+            break;
+        case 'd':
+            ls_d = 1;
+            break;
+        case 'l':
+            ls_l = 1;
+            break;
+        case 'R':
+            ls_R = 1;
+            break;
+        case 'u':
+            ls_u = 1;
+            break;
+        case ':':
+        case '?':
+            usage_ls();
+            return -1;
+            break;
+        }
+
+    }
+
+    set_signal();
+    cnid_init();
+
+    if ((argc - optind) == 0) {
+        openvol(".", &vol);
+        ad_ls_r(".", &vol);
+        closevol(&vol);
+    }
+    else {
+        int havefile = 0;
+
+        firstarg = optind;
+
+        /* First run: only print files from argv paths */
+        while(optind < argc) {
+            if (stat(argv[optind], &st) != 0)
+                goto next;
+            if (S_ISDIR(st.st_mode))
+                goto next;
+
+            havefile = 1;
+            first = 1;
+            recursion = 0;
+
+            openvol(argv[optind], &vol);
+            ad_ls_r(argv[optind], &vol);
+            closevol(&vol);
+        next:
+            optind++;
+        }
+        if (havefile && (! ls_l))
+            printf("\n");
+
+        /* Second run: print dirs */
+        optind = firstarg;
+        while(optind < argc) {
+            if (stat(argv[optind], &st) != 0)
+                goto next2;
+            if ( ! S_ISDIR(st.st_mode))
+                goto next2;
+            if ((optind > firstarg) || havefile)
+                printf("\n%s:\n", argv[optind]);
+
+            first = 1;
+            recursion = 0;
+
+            openvol(argv[optind], &vol);
+            ad_ls_r(argv[optind], &vol);
+            closevol(&vol);
+
+        next2:
+            optind++;
+        }
+
+
+    }
+
+    return 0;
+}
diff --git a/bin/ad/ad_mv.c b/bin/ad/ad_mv.c
new file mode 100644 (file)
index 0000000..4d23183
--- /dev/null
@@ -0,0 +1,477 @@
+/*
+ * Copyright (c) 2010, Frank Lahm <franklahm@googlemail.com>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libgen.h>
+
+#include <atalk/ftw.h>
+#include <atalk/adouble.h>
+#include <atalk/vfs.h>
+#include <atalk/util.h>
+#include <atalk/unix.h>
+#include <atalk/volume.h>
+#include <atalk/volinfo.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
+#include <atalk/queue.h>
+
+#include "ad.h"
+
+#define STRIP_TRAILING_SLASH(p) {                                   \
+        while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/')  \
+            *--(p).p_end = 0;                                       \
+    }
+
+static int fflg, iflg, nflg, vflg;
+
+static afpvol_t svolume, dvolume;
+static cnid_t did, pdid;
+static volatile sig_atomic_t alarmed;
+static char           *netatalk_dirs[] = {
+    ".AppleDouble",
+    ".AppleDB",
+    ".AppleDesktop",
+    NULL
+};
+
+static int copy(const char *, const char *);
+static int do_move(const char *, const char *);
+static void preserve_fd_acls(int source_fd, int dest_fd, const char *source_path,
+                             const char *dest_path);
+/*
+  Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
+  Returns pointer to name or NULL.
+*/
+static const char *check_netatalk_dirs(const char *name)
+{
+    int c;
+
+    for (c=0; netatalk_dirs[c]; c++) {
+        if ((strcmp(name, netatalk_dirs[c])) == 0)
+            return netatalk_dirs[c];
+    }
+    return NULL;
+}
+
+/*
+  SIGNAL handling:
+  catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
+*/
+
+static void sig_handler(int signo)
+{
+    alarmed = 1;
+    return;
+}
+
+static void set_signal(void)
+{
+    struct sigaction sv;
+
+    sv.sa_handler = sig_handler;
+    sv.sa_flags = SA_RESTART;
+    sigemptyset(&sv.sa_mask);
+    if (sigaction(SIGTERM, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGTERM): %s", strerror(errno));
+
+    if (sigaction(SIGINT, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGINT): %s", strerror(errno));
+
+    memset(&sv, 0, sizeof(struct sigaction));
+    sv.sa_handler = SIG_IGN;
+    sigemptyset(&sv.sa_mask);
+
+    if (sigaction(SIGABRT, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGABRT): %s", strerror(errno));
+
+    if (sigaction(SIGHUP, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGHUP): %s", strerror(errno));
+
+    if (sigaction(SIGQUIT, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGQUIT): %s", strerror(errno));
+}
+
+static void usage_mv(void)
+{
+    printf(
+        "Usage: ad mv [-f | -i | -n] [-v] source target\n"
+        "       ad mv [-f | -i | -n] [-v] source ... directory\n\n"
+        "Move files around within an AFP volume, updating the CNID\n"
+        "database as needed. If either:\n"
+        " - source or destination is not an AFP volume\n"
+        " - source volume != destinatio volume\n"
+        "the files are copied and removed from the source.\n\n"
+        "The following options are available:\n\n"
+        "   -f   Do not prompt for confirmation before overwriting the destination\n"
+        "        path.  (The -f option overrides any previous -i or -n options.)\n"
+        "   -i   Cause mv to write a prompt to standard error before moving a file\n"
+        "        that would overwrite an existing file.  If the response from the\n"
+        "        standard input begins with the character `y' or `Y', the move is\n"
+        "        attempted.  (The -i option overrides any previous -f or -n\n"
+        "        options.)\n"
+        "   -n   Do not overwrite an existing file.  (The -n option overrides any\n"
+        "        previous -f or -i options.)\n"
+        "   -v   Cause mv to be verbose, showing files after they are moved.\n"
+        );
+    exit(EXIT_FAILURE);
+}
+
+int ad_mv(int argc, char *argv[])
+{
+    size_t baselen, len;
+    int rval;
+    char *p, *endp;
+    struct stat sb;
+    int ch;
+    char path[MAXPATHLEN];
+
+
+    pdid = htonl(1);
+    did = htonl(2);
+
+    argc--;
+    argv++;
+
+    while ((ch = getopt(argc, argv, "finv")) != -1)
+        switch (ch) {
+        case 'i':
+            iflg = 1;
+            fflg = nflg = 0;
+            break;
+        case 'f':
+            fflg = 1;
+            iflg = nflg = 0;
+            break;
+        case 'n':
+            nflg = 1;
+            fflg = iflg = 0;
+            break;
+        case 'v':
+            vflg = 1;
+            break;
+        default:
+            usage_mv();
+        }
+
+    argc -= optind;
+    argv += optind;
+
+    if (argc < 2)
+        usage_mv();
+
+    set_signal();
+    cnid_init();
+    if (openvol(argv[argc - 1], &dvolume) != 0) {
+        SLOG("Error opening CNID database for source \"%s\": ", argv[argc - 1]);
+        return 1;
+    }
+
+    /*
+     * If the stat on the target fails or the target isn't a directory,
+     * try the move.  More than 2 arguments is an error in this case.
+     */
+    if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) {
+        if (argc > 2)
+            usage_mv();
+        if (openvol(argv[0], &svolume) != 0) {
+            SLOG("Error opening CNID database for destination \"%s\": ", argv[0]);
+            return 1;
+        }
+        rval = do_move(argv[0], argv[1]);
+        closevol(&svolume);
+        closevol(&dvolume);
+        return 1;
+    }
+
+    /* It's a directory, move each file into it. */
+    if (strlen(argv[argc - 1]) > sizeof(path) - 1)
+        ERROR("%s: destination pathname too long", *argv);
+
+    (void)strcpy(path, argv[argc - 1]);
+    baselen = strlen(path);
+    endp = &path[baselen];
+    if (!baselen || *(endp - 1) != '/') {
+        *endp++ = '/';
+        ++baselen;
+    }
+
+    for (rval = 0; --argc; ++argv) {
+        /*
+         * Find the last component of the source pathname.  It
+         * may have trailing slashes.
+         */
+        p = *argv + strlen(*argv);
+        while (p != *argv && p[-1] == '/')
+            --p;
+        while (p != *argv && p[-1] != '/')
+            --p;
+
+        if ((baselen + (len = strlen(p))) >= PATH_MAX) {
+            SLOG("%s: destination pathname too long", *argv);
+            rval = 1;
+        } else {
+            memmove(endp, p, (size_t)len + 1);
+            openvol(*argv, &svolume);
+
+            if (do_move(*argv, path))
+                rval = 1;
+            closevol(&svolume);
+        }
+    }
+
+exit:
+    closevol(&dvolume);
+    return rval;
+}
+
+static int do_move(const char *from, const char *to)
+{
+    struct stat sb;
+    int ask, ch, first;
+
+    /*
+     * Check access.  If interactive and file exists, ask user if it
+     * should be replaced.  Otherwise if file exists but isn't writable
+     * make sure the user wants to clobber it.
+     */
+    if (!fflg && !access(to, F_OK)) {
+
+        /* prompt only if source exist */
+        if (lstat(from, &sb) == -1) {
+            SLOG("%s: %s", from, strerror(errno));
+            return (1);
+        }
+
+        ask = 0;
+        if (nflg) {
+            if (vflg)
+                printf("%s not overwritten\n", to);
+            return (0);
+        } else if (iflg) {
+            (void)fprintf(stderr, "overwrite %s? (y/n [n]) ", to);
+            ask = 1;
+        } else if (access(to, W_OK) && !stat(to, &sb)) {
+            (void)fprintf(stderr, "override for %s? (y/n [n]) ", to);
+            ask = 1;
+        }
+        if (ask) {
+            first = ch = getchar();
+            while (ch != '\n' && ch != EOF)
+                ch = getchar();
+            if (first != 'y' && first != 'Y') {
+                (void)fprintf(stderr, "not overwritten\n");
+                return (0);
+            }
+        }
+    }
+
+    int mustcopy = 0;
+    /* 
+     * Besides the usual EXDEV we copy instead of moving if
+     * 1) source AFP volume != dest AFP volume
+     * 2) either source or dest isn't even an AFP volume
+     */
+    if (!svolume.volinfo.v_path
+        || !dvolume.volinfo.v_path
+        || strcmp(svolume.volinfo.v_path, dvolume.volinfo.v_path) != 0)
+        mustcopy = 1;
+    
+    cnid_t cnid = 0;
+    if (!mustcopy) {
+        if ((cnid = cnid_for_path(&svolume, from, &did)) == CNID_INVALID) {
+            SLOG("Couldn't resolve CNID for %s", from);
+            return -1;
+        }
+
+        if (stat(from, &sb) != 0) {
+            SLOG("Cant stat %s: %s", to, strerror(errno));
+            return -1;
+        }
+
+        if (rename(from, to) != 0) {
+            if (errno == EXDEV) {
+                mustcopy = 1;
+                char path[MAXPATHLEN];
+
+                /*
+                 * If the source is a symbolic link and is on another
+                 * filesystem, it can be recreated at the destination.
+                 */
+                if (lstat(from, &sb) == -1) {
+                    SLOG("%s: %s", from, strerror(errno));
+                    return (-1);
+                }
+                if (!S_ISLNK(sb.st_mode)) {
+                    /* Can't mv(1) a mount point. */
+                    if (realpath(from, path) == NULL) {
+                        SLOG("cannot resolve %s: %s: %s", from, path, strerror(errno));
+                        return (1);
+                    }
+                }
+            } else { /* != EXDEV */
+                SLOG("rename %s to %s: %s", from, to, strerror(errno));
+                return (1);
+            }
+        } /* rename != 0*/
+        
+        switch (sb.st_mode & S_IFMT) {
+        case S_IFREG:
+            if (dvolume.volume.vfs->vfs_renamefile(&dvolume.volume, -1, from, to) != 0) {
+                SLOG("Error moving adouble file for %s", from);
+                return -1;
+            }
+            break;
+        case S_IFDIR:
+            break;
+        default:
+            SLOG("Not a file or dir: %s", from);
+            return -1;
+        }
+    
+        /* get CNID of new parent dir */
+        cnid_t newpdid, newdid;
+        if ((newdid = cnid_for_paths_parent(&dvolume, to, &newpdid)) == CNID_INVALID) {
+            SLOG("Couldn't resolve CNID for parent of %s", to);
+            return -1;
+        }
+
+        if (stat(to, &sb) != 0) {
+            SLOG("Cant stat %s: %s", to, strerror(errno));
+            return 1;
+        }
+
+        char *p = strdup(to);
+        char *name = basename(p);
+        if (cnid_update(dvolume.volume.v_cdb, cnid, &sb, newdid, name, strlen(name)) != 0) {
+            SLOG("Cant update CNID for: %s", to);
+            return 1;
+        }
+        free(p);
+
+        struct adouble ad;
+        ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options);
+        if (ad_open_metadata(to, S_ISDIR(sb.st_mode) ? ADFLAGS_DIR : 0, O_RDWR, &ad) != 0) {
+            SLOG("Error opening adouble for: %s", to);
+            return 1;
+        }
+        ad_setid(&ad, sb.st_dev, sb.st_ino, cnid, newdid, dvolume.db_stamp);
+        ad_flush(&ad);
+        ad_close_metadata(&ad);
+
+        if (vflg)
+            printf("%s -> %s\n", from, to);
+        return (0);
+    }
+    
+    if (mustcopy)
+        return copy(from, to);
+
+    /* If we get here it's an error */
+    return -1;
+}
+
+static int copy(const char *from, const char *to)
+{
+    struct stat sb;
+    int pid, status;
+
+    if (lstat(to, &sb) == 0) {
+        /* Destination path exists. */
+        if (S_ISDIR(sb.st_mode)) {
+            if (rmdir(to) != 0) {
+                SLOG("rmdir %s: %s", to, strerror(errno));
+                return (1);
+            }
+        } else {
+            if (unlink(to) != 0) {
+                SLOG("unlink %s: %s", to, strerror(errno));
+                return (1);
+            }
+        }
+    } else if (errno != ENOENT) {
+        SLOG("%s: %s", to, strerror(errno));
+        return (1);
+    }
+
+    /* Copy source to destination. */
+    if (!(pid = fork())) {
+        execl(_PATH_AD, "ad", "cp", vflg ? "-Rpv" : "-Rp", from, to, (char *)NULL);
+        _exit(1);
+    }
+    while ((waitpid(pid, &status, 0)) == -1) {
+        if (errno == EINTR)
+            continue;
+        SLOG("%s cp -R %s %s: waitpid: %s", _PATH_AD, from, to, strerror(errno));
+        return (1);
+    }
+    if (!WIFEXITED(status)) {
+        SLOG("%s cp -R %s %s: did not terminate normally", _PATH_AD, from, to);
+        return (1);
+    }
+    switch (WEXITSTATUS(status)) {
+    case 0:
+        break;
+    default:
+        SLOG("%s cp -R %s %s: terminated with %d (non-zero) status",
+             _PATH_AD, from, to, WEXITSTATUS(status));
+        return (1);
+    }
+
+    /* Delete the source. */
+    if (!(pid = fork())) {
+        execl(_PATH_AD, "ad", "rm", "-R", from, (char *)NULL);
+        _exit(1);
+    }
+    while ((waitpid(pid, &status, 0)) == -1) {
+        if (errno == EINTR)
+            continue;
+        SLOG("%s rm -R %s: waitpid: %s", _PATH_AD, from, strerror(errno));
+        return (1);
+    }
+    if (!WIFEXITED(status)) {
+        SLOG("%s rm -R %s: did not terminate normally", _PATH_AD, from);
+        return (1);
+    }
+    switch (WEXITSTATUS(status)) {
+    case 0:
+        break;
+    default:
+        SLOG("%s rm -R %s: terminated with %d (non-zero) status",
+              _PATH_AD, from, WEXITSTATUS(status));
+        return (1);
+    }
+    return 0;
+}
+
+static void
+preserve_fd_acls(int source_fd,
+                 int dest_fd,
+                 const char *source_path,
+                 const char *dest_path)
+{
+    ;
+}
diff --git a/bin/ad/ad_rm.c b/bin/ad/ad_rm.c
new file mode 100644 (file)
index 0000000..b9ef2c3
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2010, Frank Lahm <franklahm@googlemail.com>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atalk/ftw.h>
+#include <atalk/adouble.h>
+#include <atalk/vfs.h>
+#include <atalk/util.h>
+#include <atalk/unix.h>
+#include <atalk/volume.h>
+#include <atalk/volinfo.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
+#include <atalk/queue.h>
+
+#include "ad.h"
+
+#define STRIP_TRAILING_SLASH(p) {                                   \
+        while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/')  \
+            *--(p).p_end = 0;                                       \
+    }
+
+static afpvol_t volume;
+
+static cnid_t did, pdid;
+static int Rflag;
+static volatile sig_atomic_t alarmed;
+static int badrm, rval;
+
+static char           *netatalk_dirs[] = {
+    ".AppleDB",
+    ".AppleDesktop",
+    NULL
+};
+
+/* Forward declarations */
+static int rm(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf);
+
+/*
+  Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
+  Returns pointer to name or NULL.
+*/
+static const char *check_netatalk_dirs(const char *name)
+{
+    int c;
+
+    for (c=0; netatalk_dirs[c]; c++) {
+        if ((strcmp(name, netatalk_dirs[c])) == 0)
+            return netatalk_dirs[c];
+    }
+    return NULL;
+}
+
+static void upfunc(void)
+{
+    did = pdid;
+}
+
+/*
+  SIGNAL handling:
+  catch SIGINT and SIGTERM which cause clean exit. Ignore anything else.
+*/
+
+static void sig_handler(int signo)
+{
+    alarmed = 1;
+    return;
+}
+
+static void set_signal(void)
+{
+    struct sigaction sv;
+
+    sv.sa_handler = sig_handler;
+    sv.sa_flags = SA_RESTART;
+    sigemptyset(&sv.sa_mask);
+    if (sigaction(SIGTERM, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGTERM): %s", strerror(errno));
+
+    if (sigaction(SIGINT, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGINT): %s", strerror(errno));
+
+    memset(&sv, 0, sizeof(struct sigaction));
+    sv.sa_handler = SIG_IGN;
+    sigemptyset(&sv.sa_mask);
+
+    if (sigaction(SIGABRT, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGABRT): %s", strerror(errno));
+
+    if (sigaction(SIGHUP, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGHUP): %s", strerror(errno));
+
+    if (sigaction(SIGQUIT, &sv, NULL) < 0)
+        ERROR("error in sigaction(SIGQUIT): %s", strerror(errno));
+}
+
+static void usage_rm(void)
+{
+    printf(
+        "Usage: ad rm [-vR] <file|dir> [<file|dir> ...]\n\n"
+        "The rm utility attempts to remove the non-directory type files specified\n"
+        "on the command line.\n"
+        "If the files and directories reside on an AFP volume, the corresponding\n"
+        "CNIDs are deleted from the volumes database.\n\n"
+        "The options are as follows:\n\n"
+        "   -R   Attempt to remove the file hierarchy rooted in each file argument.\n"
+        "   -v   Be verbose when deleting files, showing them as they are removed.\n"
+        );
+    exit(EXIT_FAILURE);
+}
+
+int ad_rm(int argc, char *argv[])
+{
+    int ch;
+
+    pdid = htonl(1);
+    did = htonl(2);
+
+    while ((ch = getopt(argc, argv, "vR")) != -1)
+        switch (ch) {
+        case 'R':
+            Rflag = 1;
+            break;
+        case 'v':
+            vflag = 1;
+            break;
+        default:
+            usage_rm();
+            break;
+        }
+    argc -= optind;
+    argv += optind;
+
+    if (argc < 1)
+        usage_rm();
+
+    set_signal();
+    cnid_init();
+
+    /* Set end of argument list */
+    argv[argc] = NULL;
+
+    for (int i = 0; argv[i] != NULL; i++) {
+        /* Load .volinfo file for source */
+        openvol(argv[i], &volume);
+
+        if (nftw(argv[i], rm, upfunc, 20, FTW_DEPTH | FTW_PHYS) == -1) {
+            if (alarmed) {
+                SLOG("...break");
+            } else {
+                SLOG("Error: %s", argv[i]);
+            }
+            closevol(&volume);
+        }
+    }
+    return rval;
+}
+
+static int rm(const char *path,
+              const struct stat *statp,
+              int tflag,
+              struct FTW *ftw)
+{
+    cnid_t cnid;
+
+    if (alarmed)
+        return -1;
+
+    const char *dir = strrchr(path, '/');
+    if (dir == NULL)
+        dir = path;
+    else
+        dir++;
+    if (check_netatalk_dirs(dir) != NULL)
+        return FTW_SKIP_SUBTREE;
+
+    switch (statp->st_mode & S_IFMT) {
+
+    case S_IFLNK:
+        if (volume.volinfo.v_path) {
+            if ((volume.volinfo.v_adouble == AD_VERSION2)
+                && (strstr(path, ".AppleDouble") != NULL)) {
+                /* symlink inside adouble dir */
+                if (unlink(path) != 0)
+                    badrm = rval = 1;
+                break;
+            }
+
+            /* Get CNID of Parent and add new childir to CNID database */
+            pdid = did;
+            if ((cnid = cnid_for_path(&volume, path, &did)) == CNID_INVALID) {
+                SLOG("Error resolving CNID for %s", path);
+                return -1;
+            }
+            if (cnid_delete(volume.volume.v_cdb, cnid) != 0) {
+                SLOG("Error removing CNID %u for %s", ntohl(cnid), path);
+                return -1;
+            }
+        }
+
+        if (unlink(path) != 0) {
+            badrm = rval = 1;
+            break;
+        }
+
+        break;
+
+    case S_IFDIR:
+        if (!Rflag) {
+            SLOG("%s is a directory", path);
+            return FTW_SKIP_SUBTREE;
+        }
+
+        if (volume.volinfo.v_path) {
+            if ((volume.volinfo.v_adouble == AD_VERSION2)
+                && (strstr(path, ".AppleDouble") != NULL)) {
+                /* should be adouble dir itself */
+                if (rmdir(path) != 0) {
+                    SLOG("Error removing dir \"%s\": %s", path, strerror(errno));
+                    badrm = rval = 1;
+                    return -1;
+                }
+                break;
+            }
+
+            /* Get CNID of Parent and add new childir to CNID database */
+            if ((did = cnid_for_path(&volume, path, &pdid)) == CNID_INVALID) {
+                SLOG("Error resolving CNID for %s", path);
+                return -1;
+            }
+            if (cnid_delete(volume.volume.v_cdb, did) != 0) {
+                SLOG("Error removing CNID %u for %s", ntohl(did), path);
+                return -1;
+            }
+        }
+
+        if (rmdir(path) != 0) {
+            SLOG("Error removing dir \"%s\": %s", path, strerror(errno));
+            badrm = rval = 1;
+            return -1;
+        }
+
+        break;
+
+    case S_IFBLK:
+    case S_IFCHR:
+        SLOG("%s is a device file.", path);
+        badrm = rval = 1;
+        break;
+
+    case S_IFSOCK:
+        SLOG("%s is a socket.", path);
+        badrm = rval = 1;
+        break;
+
+    case S_IFIFO:
+        SLOG("%s is a FIFO.", path);
+        badrm = rval = 1;
+        break;
+
+    default:
+        if (volume.volinfo.v_path) {
+            if ((volume.volinfo.v_adouble == AD_VERSION2)
+                && (strstr(path, ".AppleDouble") != NULL)) {
+                /* file in adouble dir */
+                if (unlink(path) != 0)
+                    badrm = rval = 1;
+                break;
+            }
+
+            /* Get CNID of Parent and add new childir to CNID database */
+            pdid = did;
+            if ((cnid = cnid_for_path(&volume, path, &did)) == CNID_INVALID) {
+                SLOG("Error resolving CNID for %s", path);
+                return -1;
+            }
+            if (cnid_delete(volume.volume.v_cdb, cnid) != 0) {
+                SLOG("Error removing CNID %u for %s", ntohl(cnid), path);
+                return -1;
+            }
+
+            /* Ignore errors, because with -R adouble stuff is always alread gone */
+            volume.volume.vfs->vfs_deletefile(&volume.volume, -1, path);
+        }
+
+        if (unlink(path) != 0) {
+            badrm = rval = 1;
+            break;
+        }
+
+        break;
+    }
+
+    if (vflag && !badrm)
+        (void)printf("%s\n", path);
+
+    return 0;
+}
diff --git a/bin/ad/ad_util.c b/bin/ad/ad_util.c
new file mode 100644 (file)
index 0000000..4a8ff11
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ * Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <string.h>
+#include <libgen.h>
+
+#ifdef HAVE_SOLARIS_ACLS
+#include <sys/acl.h>
+#endif  /* HAVE_SOLARIS_ACLS */
+
+#ifdef HAVE_POSIX_ACLS
+#include <sys/types.h>
+#include <sys/acl.h>
+#endif /* HAVE_POSIX_ACLS */
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/volinfo.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
+#include <atalk/logger.h>
+#include <atalk/errchk.h>
+#include <atalk/unicode.h>
+
+#include "ad.h"
+
+int log_verbose;             /* Logging flag */
+
+void _log(enum logtype lt, char *fmt, ...)
+{
+    int len;
+    static char logbuffer[1024];
+    va_list args;
+
+    if ( (lt == STD) || (log_verbose == 1)) {
+        va_start(args, fmt);
+        len = vsnprintf(logbuffer, 1023, fmt, args);
+        va_end(args);
+        logbuffer[1023] = 0;
+
+        printf("%s\n", logbuffer);
+    }
+}
+
+/*!
+ * Load volinfo and initialize struct vol
+ *
+ * Only opens "dbd" volumes !
+ *
+ * @param path   (r)  path to evaluate
+ * @param vol    (rw) structure to initialize
+ *
+ * @returns 0 on success, exits on error
+ */
+int openvol(const char *path, afpvol_t *vol)
+{
+    int flags = 0;
+
+    memset(vol, 0, sizeof(afpvol_t));
+
+    /* try to find a .AppleDesktop/.volinfo */
+    if (loadvolinfo((char *)path, &vol->volinfo) != 0)
+        return -1;
+
+    if (STRCMP(vol->volinfo.v_cnidscheme, != , "dbd"))
+        ERROR("\"%s\" isn't a \"dbd\" CNID volume!", vol->volinfo.v_path);
+
+    if (vol_load_charsets(&vol->volinfo) == -1)
+        ERROR("Error loading charsets!");
+
+    /* Sanity checks to ensure we can touch this volume */
+    if (vol->volinfo.v_adouble != AD_VERSION2)
+        ERROR("Unsupported adouble versions: %u", vol->volinfo.v_adouble);
+
+    if (vol->volinfo.v_vfs_ea != AFPVOL_EA_SYS)
+        ERROR("Unsupported Extended Attributes option: %u", vol->volinfo.v_vfs_ea);
+
+    /* initialize sufficient struct vol for VFS initialisation */
+    vol->volume.v_adouble = AD_VERSION2;
+    vol->volume.v_vfs_ea = AFPVOL_EA_SYS;
+    initvol_vfs(&vol->volume);
+
+    if ((vol->volinfo.v_flags & AFPVOL_NODEV))
+        flags |= CNID_FLAG_NODEV;
+
+    if ((vol->volume.v_cdb = cnid_open(vol->volinfo.v_path,
+                                       0000,
+                                       "dbd",
+                                       flags,
+                                       vol->volinfo.v_dbd_host,
+                                       vol->volinfo.v_dbd_port)) == NULL)
+        ERROR("Cant initialize CNID database connection for %s", vol->volinfo.v_path);
+
+    cnid_getstamp(vol->volume.v_cdb,
+                  vol->db_stamp,
+                  sizeof(vol->db_stamp));
+    
+    return 0;
+}
+
+void closevol(afpvol_t *vol)
+{
+    if (vol->volume.v_cdb)
+        cnid_close(vol->volume.v_cdb);
+
+    memset(vol, 0, sizeof(afpvol_t));
+}
+
+/*
+  Taken form afpd/desktop.c
+*/
+char *utompath(const struct volinfo *volinfo, const char *upath)
+{
+    static char  mpath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
+    char         *m;
+    const char   *u;
+    uint16_t     flags = CONV_IGNORE | CONV_UNESCAPEHEX;
+    size_t       outlen;
+
+    if (!upath)
+        return NULL;
+
+    m = mpath;
+    u = upath;
+    outlen = strlen(upath);
+
+    if ((volinfo->v_casefold & AFPVOL_UTOMUPPER))
+        flags |= CONV_TOUPPER;
+    else if ((volinfo->v_casefold & AFPVOL_UTOMLOWER))
+        flags |= CONV_TOLOWER;
+
+    if ((volinfo->v_flags & AFPVOL_EILSEQ)) {
+        flags |= CONV__EILSEQ;
+    }
+
+    /* convert charsets */
+    if ((size_t)-1 == ( outlen = convert_charset(volinfo->v_volcharset,
+                                                 CH_UTF8_MAC,
+                                                 volinfo->v_maccharset,
+                                                 u, outlen, mpath, MAXPATHLEN, &flags)) ) {
+        SLOG("Conversion from %s to %s for %s failed.",
+             volinfo->v_volcodepage, volinfo->v_maccodepage, u);
+        return NULL;
+    }
+
+    return(m);
+}
+
+
+/*!
+ * Convert dot encoding of basename _in place_
+ *
+ * path arg can be "[/][dir/ | ...]filename". It will be converted in place
+ * possible encoding ".file" as ":2efile" which means the result will be
+ * longer then the original which means provide a big enough buffer.
+ *
+ * @param svol   (r)  source volume
+ * @param dvol   (r)  destinatio volume
+ * @param path   (rw) path to convert _in place_
+ * @param buflen (r)  size of path buffer (max strlen == buflen -1)
+ *
+ * @returns 0 on sucess, -1 on error
+ */
+int convert_dots_encoding(const afpvol_t *svol, const afpvol_t *dvol, char *path, size_t buflen)
+{
+    static charset_t from = (charset_t) -1;
+    static char buf[MAXPATHLEN+2];
+    char *bname = stripped_slashes_basename(path);
+    int pos = bname - path;
+    uint16_t flags = 0;
+
+    if ( ! svol->volinfo.v_path) {
+        /* no source volume: escape special chars (eg ':') */
+        from = dvol->volinfo.v_volcharset; /* src = dst charset */
+        flags |= CONV_ESCAPEHEX;
+    } else {
+        from = svol->volinfo.v_volcharset;
+    }
+
+    if ( (svol->volinfo.v_path)
+         && ! (svol->volinfo.v_flags & AFPVOL_USEDOTS)
+         && (dvol->volinfo.v_flags & AFPVOL_USEDOTS)) {
+        /* source is without dots, destination is with */
+        flags |= CONV_UNESCAPEHEX;
+    } else if (! (dvol->volinfo.v_flags & AFPVOL_USEDOTS)) {
+        flags |= CONV_ESCAPEDOTS;
+    }
+
+    int len = convert_charset(from,
+                              dvol->volinfo.v_volcharset,
+                              dvol->volinfo.v_maccharset,
+                              bname, strlen(bname),
+                              buf, MAXPATHLEN,
+                              &flags);
+    if (len == -1)
+        return -1;
+
+    if (strlcpy(bname, buf, MAXPATHLEN - pos) > MAXPATHLEN - pos)
+        return -1;
+    return 0;
+}
+
+/*!
+ * ResolvesCNID of a given paths
+ *
+ * path might be:
+ * (a) relative:
+ *     "dir/subdir" with cwd: "/afp_volume/topdir"
+ * (b) absolute:
+ *     "/afp_volume/dir/subdir"
+ *
+ * path MUST be pointing inside vol, this is usually the case as vol has been build from
+ * path using loadvolinfo and friends.
+ *
+ * @param vol  (r) pointer to afpvol_t
+ * @param path (r) path, see above
+ * @param did  (rw) parent CNID of returned CNID
+ *
+ * @returns CNID of path
+ */
+cnid_t cnid_for_path(const afpvol_t *vol,
+                     const char *path,
+                     cnid_t *did)
+{
+    EC_INIT;
+
+    cnid_t cnid;
+    bstring rpath = NULL;
+    bstring statpath = NULL;
+    struct bstrList *l = NULL;
+    struct stat st;
+
+    cnid = htonl(2);
+
+    EC_NULL(rpath = rel_path_in_vol(path, vol->volinfo.v_path));
+    EC_NULL(statpath = bfromcstr(vol->volinfo.v_path));
+    EC_ZERO(bcatcstr(statpath, "/"));
+
+    l = bsplit(rpath, '/');
+    for (int i = 0; i < l->qty ; i++) {
+        *did = cnid;
+
+        EC_ZERO(bconcat(statpath, l->entry[i]));
+        EC_ZERO_LOGSTR(lstat(cfrombstr(statpath), &st),
+                       "lstat(rpath: %s, elem: %s): %s: %s",
+                       cfrombstr(rpath), cfrombstr(l->entry[i]),
+                       cfrombstr(statpath), strerror(errno));
+
+        if ((cnid = cnid_add(vol->volume.v_cdb,
+                             &st,
+                             *did,
+                             cfrombstr(l->entry[i]),
+                             blength(l->entry[i]),
+                             0)) == CNID_INVALID) {
+            EC_FAIL;
+        }
+        EC_ZERO(bcatcstr(statpath, "/"));
+    }
+
+EC_CLEANUP:
+    bdestroy(rpath);
+    bstrListDestroy(l);
+    bdestroy(statpath);
+    if (ret != 0)
+        return CNID_INVALID;
+
+    return cnid;
+}
+
+/*!
+ * Resolves CNID of a given paths parent directory
+ *
+ * path might be:
+ * (a) relative:
+ *     "dir/subdir" with cwd: "/afp_volume/topdir"
+ * (b) absolute:
+ *     "/afp_volume/dir/subdir"
+ *
+ * path MUST be pointing inside vol, this is usually the case as vol has been build from
+ * path using loadvolinfo and friends.
+ *
+ * @param vol  (r) pointer to afpvol_t
+ * @param path (r) path, see above
+ * @param did  (rw) parent CNID of returned CNID
+ *
+ * @returns CNID of path
+ */
+cnid_t cnid_for_paths_parent(const afpvol_t *vol,
+                             const char *path,
+                             cnid_t *did)
+{
+    EC_INIT;
+
+    cnid_t cnid;
+    bstring rpath = NULL;
+    bstring statpath = NULL;
+    struct bstrList *l = NULL;
+    struct stat st;
+
+    *did = htonl(1);
+    cnid = htonl(2);
+
+    EC_NULL(rpath = rel_path_in_vol(path, vol->volinfo.v_path));
+    EC_NULL(statpath = bfromcstr(vol->volinfo.v_path));
+
+    l = bsplit(rpath, '/');
+    if (l->qty == 1)
+        /* only one path element, means parent dir cnid is volume root = 2 */
+        goto EC_CLEANUP;
+    for (int i = 0; i < (l->qty - 1); i++) {
+        *did = cnid;
+        EC_ZERO(bconcat(statpath, l->entry[i]));
+        EC_ZERO_LOGSTR(lstat(cfrombstr(statpath), &st),
+                       "lstat(rpath: %s, elem: %s): %s: %s",
+                       cfrombstr(rpath), cfrombstr(l->entry[i]),
+                       cfrombstr(statpath), strerror(errno));
+
+        if ((cnid = cnid_add(vol->volume.v_cdb,
+                             &st,
+                             *did,
+                             cfrombstr(l->entry[i]),
+                             blength(l->entry[i]),
+                             0)) == CNID_INVALID) {
+            EC_FAIL;
+        }
+        EC_ZERO(bcatcstr(statpath, "/"));
+    }
+
+EC_CLEANUP:
+    bdestroy(rpath);
+    bstrListDestroy(l);
+    bdestroy(statpath);
+    if (ret != 0)
+        return CNID_INVALID;
+
+    return cnid;
+}
+
diff --git a/bin/afile/Makefile.am b/bin/afile/Makefile.am
deleted file mode 100644 (file)
index 376eab6..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# Makefile.am for bin/afile/
-
-INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/sys
-
-bin_PROGRAMS = afile achfile
-
-afile_SOURCES = afile.c common.c common.h
-achfile_SOURCES = achfile.c common.c common.h
diff --git a/bin/afile/achfile.c b/bin/afile/achfile.c
deleted file mode 100644 (file)
index 99133cc..0000000
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * $Id: achfile.c,v 1.7 2009-10-14 01:38:28 didg Exp $
- *
-    afile - determine the MacOS creator/type of files
-
-    Copyright (C) 2001 Sebastian Rittau.
-    All rights reserved.
-
-    This file may be distributed and/or modfied under the terms of the
-    following license:
-
-    Redistribution and use in source and binary forms, with or without
-    modification, are permitted provided that the following conditions
-    are met:
-    1. Redistributions of source code must retain the above copyright
-       notice, this list of conditions and the following disclaimer.
-    2. Redistributions in binary form must reproduce the above copyright
-       notice, this list of conditions and the following disclaimer in the
-       documentation and/or other materials provided with the distribution.
-    3. Neither the name of the author nor the names of its contributors
-       may be used to endorse or promote products derived from this software
-       without specific prior written permission.
-
-    THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-    ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-    OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-    SUCH DAMAGE.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif /* HAVE_FCNTL_H */
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif /* HAVE_UNISTD_H */
-
-#include <atalk/adouble.h>
-
-#include "common.h"
-
-/* Global Variables */
-static const char *type    = NULL;
-static const char *creator = NULL;
-
-
-/* Print usage information. */
-static void usage(char *prog)
-{
-  fprintf(stderr, "Usage: %s [-t TYPE] [-c CREATOR] FILE ...\n", prog);
-}
-
-/* Print extensive help. */
-static void help(char *prog)
-{
-  usage(prog);
-  fprintf(stderr,
-         "\n"
-         "Change the MacOS creator/type of FILEs.\n"
-         "\n"
-         "  -t, --type=TYPE        choose type\n"
-         "  -c, --creator=CREATOR  choose creator\n"
-         "  -h, --help             show this help and exit\n"
-         "  -v, --version          show version information and exit\n");
-}
-
-/* Print the version. */
-static void version(void)
-{
-  fprintf(stderr, "achfile (netatalk " VERSION ")\n");
-}
-
-/* Argument Handling
- * known options: -t, -c, -h, -v
- * known long options: --help, --version
- */
-#define OPTSTRING "t:c:hv-:"
-static const char *get_long_arg(int argc, char *argv[], const char *arg, const char *oa) {
-  switch (*oa) {
-  case '=':
-    return &oa[1];
-  case '\0':
-    if (optind == argc) {
-      fprintf(stderr, "%s: option \'%s\' requires an argument\n", argv[0], arg);
-      return NULL;
-    }
-    return argv[optind++];
-  default:
-    fprintf(stderr, "%s: unrecognized option \'%s\'\n", argv[0], arg);
-    usage(argv[0]);
-    return NULL;
-  }
-}
-
-static int parse_args(int argc, char *argv[])
-{
-  int c;
-  const char *longarg;
-
-  /* iterate through the command line */
-  while ((c = getopt(argc, argv, OPTSTRING)) != -1) {
-    switch (c) {
-    case 'h':
-      help(argv[0]);
-      exit(0);
-    case 'v':
-      version();
-      exit(0);
-    case 't':
-      type = optarg;
-      break;
-    case 'c':
-      creator = optarg;
-      break;
-    case '-':
-      if (strcmp(optarg, "help") == 0) {
-       help(argv[0]);
-       exit(0);
-      }
-      if (strcmp(optarg, "version") == 0) {
-       version();
-       exit(0);
-      }
-      if (strncmp(optarg, "type", 4) == 0) {
-       longarg = get_long_arg(argc, argv, "type", &optarg[4]);
-       if (!longarg)
-         return -1;
-       type = longarg;
-      } else if (strncmp(optarg, "creator", 7) == 0) {
-       longarg = get_long_arg(argc, argv, "creator", &optarg[7]);
-       if (!longarg)
-         return -1;
-       creator = longarg;
-      }
-      break;
-    default:
-      usage(argv[0]);
-      return -1;
-    }
-  }
-
-  /* At least one file argument is required. */
-  if (argc == optind) {
-    usage(argv[0]);
-    return -1;
-  }
-
-  /* Either type or creator is required. */
-  if (!type && !creator) {
-    fprintf(stderr, "achfile: either type or creator must be specified\n");
-    return -1;
-  }
-
-  /* Type and creator must be exactly four characters long. */
-  if ((type && strlen(type) != 4) || (creator && strlen(creator) != 4)) {
-    fprintf(stderr, "achfile: type and creator must be four character IDs\n");
-    return -1;
-  }
-
-  return 0;
-}
-
-
-/* Change the owner/creator of each file specified on the command line. */
-static int handle_file(const char *filename)
-{
-  int fd;
-  struct stat statbuf;
-  char *adname;
-  ssize_t sz;
-  char buf[AD_DATASZ];
-  struct adouble *ad = (struct adouble *) &buf;
-
-  if (stat(filename, &statbuf) == -1) {
-    fprintf(stderr, "achfile:%s: %s\n", filename, strerror(errno));
-    return -1;
-  }
-
-  adname = dataname_to_adname(filename);
-  fd = open(adname, O_RDWR, 0);
-  if (fd == -1) {
-    if (errno == ENOENT)
-      fprintf(stderr, "achfile:%s: no resource fork\n", filename);
-    else
-      fprintf(stderr, "achfile:%s: %s\n", adname, strerror(errno));
-    free(adname);
-    return -1;
-  }
-  sz = read(fd, buf, AD_DATASZ);
-  if (sz == -1) {
-    fprintf(stderr, "achfile:%s: %s\n", adname, strerror(errno));
-    free(adname);
-    close(fd);
-    return -1;
-  } else if (sz < AD_DATASZ) {
-    fprintf(stderr, "achfile:%s: corrupt resource fork\n", filename);
-    free(adname);
-    close(fd);
-    return -1;
-  }
-  if ( ntohl(ad->ad_magic) != AD_MAGIC) {
-    fprintf(stderr, "achfile:%s: corrupt resource fork\n", filename);
-    free(adname);
-    close(fd);
-    return -1;
-  }
-
-  /* Change Type */
-  if (type) {
-    buf[ADEDOFF_FINDERI + 0] = type[0];
-    buf[ADEDOFF_FINDERI + 1] = type[1];
-    buf[ADEDOFF_FINDERI + 2] = type[2];
-    buf[ADEDOFF_FINDERI + 3] = type[3];
- }
-
-  /* Change Creator */
-  if (creator) {
-    buf[ADEDOFF_FINDERI + 4] = creator[0];
-    buf[ADEDOFF_FINDERI + 5] = creator[1];
-    buf[ADEDOFF_FINDERI + 6] = creator[2];
-    buf[ADEDOFF_FINDERI + 7] = creator[3];
- }
-
-  /* Write file back to disk. */
-  if (lseek(fd, 0, SEEK_SET) == -1) {
-    fprintf(stderr, "achfile:%s: %s\n", adname, strerror(errno));
-    free(adname);
-    close(fd);
-    return -1;
-  }
-  if (write(fd, &buf, AD_DATASZ) < AD_DATASZ) {
-    fprintf(stderr, "achfile:%s: %s\n", adname, strerror(errno));
-    free(adname);
-    close(fd);
-    return -1;
-  }
-
-  /* Clean Up */
-  free(adname);
-  close(fd);
-
-  return 0;
-}
-
-
-int main(int argc, char *argv[])
-{
-  /* argument handling */
-  if (parse_args(argc, argv) == -1)
-    return 1;
-
-  while (optind < argc)
-    handle_file(argv[optind++]);
-
-  return 0;
-}
diff --git a/bin/afile/afile.c b/bin/afile/afile.c
deleted file mode 100644 (file)
index e70c850..0000000
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * $Id: afile.c,v 1.7 2009-10-14 01:38:28 didg Exp $
- *
-    afile - determine the MacOS creator/type of files
-
-    Copyright (C) 2001 Sebastian Rittau.
-    All rights reserved.
-
-    This file may be distributed and/or modfied under the terms of the
-    following license:
-
-    Redistribution and use in source and binary forms, with or without
-    modification, are permitted provided that the following conditions
-    are met:
-    1. Redistributions of source code must retain the above copyright
-       notice, this list of conditions and the following disclaimer.
-    2. Redistributions in binary form must reproduce the above copyright
-       notice, this list of conditions and the following disclaimer in the
-       documentation and/or other materials provided with the distribution.
-    3. Neither the name of the author nor the names of its contributors
-       may be used to endorse or promote products derived from this software
-       without specific prior written permission.
-
-    THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-    ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-    OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-    SUCH DAMAGE.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif /* HAVE_UNISTD_H */
-
-#include <atalk/adouble.h>
-
-#include "common.h"
-
-/* Possible return types. These are taken from the original afile for
- * compatibility.
- */
-#define RET_OK                0 /* everything went fine */
-#define RET_NO_SUCH_FILE      1 /* file doesn't exist */
-#define RET_UNREADABLE        2 /* file is unreadable */
-#define RET_IS_DIR            3 /* file is a directory */
-#define RET_NO_DF             4 /* there is no corresponding data fork */
-#define RET_INVALID_DF        5 /* data fork exists but can't be read */
-#define RET_NO_AD             6 /* AppleDouble file doesn't exist */
-#define RET_INVALID_AD        7 /* AppleDouble file exists but can't be read */
-#define RET_SHORT_AD          8 /* AppleDouble file is too short */
-#define RET_BAD_MAGIC         9 /* AppleDouble file has a bad magic value */
-#define RET_BAD_ARGS         99 /* bad command line options */
-
-
-/* Global Variables */
-static int showall = 0;
-
-
-/* Print usage information. */
-static void usage(char *prog)
-{
-  fprintf(stderr, "Usage: %s [-a] FILE ...\n", prog);
-}
-
-/* Print extensive help. */
-static void help(char *prog)
-{
-  usage(prog);
-  fprintf(stderr,
-         "\n"
-         "Determine the MacOS creator/type of FILEs.\n"
-         "\n"
-         "  -a, --show-all    also show unknown files and directories\n"
-         "  -h, --help        show this help and exit\n"
-         "  -v, --version     show version information and exit\n");
-}
-
-/* Print the version. */
-static void version(void)
-{
-  fprintf(stderr, "afile (netatalk " VERSION ")\n");
-}
-
-/* Argument Handling
- * known options: -a, -h, -v
- * known long options: --show-all, --help, --version
- */
-#define OPTSTRING "ahv-:"
-static int parse_args(int argc, char *argv[])
-{
-  int c;
-
-  /* iterate through the command line */
-  while ((c = getopt(argc, argv, OPTSTRING)) != -1) {
-    switch (c) {
-    case 'h':
-      help(argv[0]);
-      exit(0);
-    case 'v':
-      version();
-      exit(0);
-    case 'a':
-      showall = 1;
-      break;
-    case '-':
-      if (strcmp(optarg, "help") == 0) {
-       help(argv[0]);
-       exit(0);
-      }
-      if (strcmp(optarg, "version") == 0) {
-       version();
-       exit(0);
-      }
-      if (strcmp(optarg, "show-all") == 0)
-       showall = 1;
-      break;
-    default:
-      usage(argv[0]);
-      return -1;
-    }
-  }
-
-  /* At least one file argument is required. */
-  if (argc == optind) {
-    usage(argv[0]);
-    return -1;
-  }
-
-  return 0;
-}
-
-
-/* Print the creator/type as taken from the supplied character stream, which
- * must be a AppleDouble file header.
- */
-static void print_type(const char *filename, const char *creator, const char *type)
-{
-  printf("%4.4s %4.4s %s\n", creator, type, filename);
-}
-
-
-static int handle_ad(struct AFile *rfile)
-{
-  int fd;
-  char *dataname;
-
-  dataname = adname_to_dataname(afile_filename(rfile));
-
-  /* Try to open data file. */
-  fd = open(dataname, O_RDONLY);
-  free(dataname);
-  if (fd == -1) {
-    if (errno == ENOENT)
-      return RET_NO_DF;      /* There is no data fork. */
-    else
-      return RET_INVALID_DF; /* The data fork can't be read. */
-  }
-  /* FIXME: stat */
-
-  close(fd);
-
-  return RET_OK;
-}
-
-
-static int handle_datafile(struct AFile *datafile)
-{
-  int ret;
-  char *adname;
-  struct AFile *rfile;
-
-  /* Try to load AppleDouble file. */
-  adname = dataname_to_adname(afile_filename(datafile));
-  rfile = afile_new(adname);
-  if (!rfile) {
-    if (errno == ENOENT) {
-      free(adname);
-      if (showall)
-       printf("unknown %s\n", afile_filename(datafile));
-      return RET_NO_AD;
-    }
-    fprintf(stderr, "afile:%s: %s\n", adname, strerror(errno));
-    free(adname);
-    return RET_INVALID_AD;
-  }
-  free(adname);
-
-  /* Check if this is really an AppleDouble file. */
-  if (afile_is_ad(rfile)) {
-    print_type(afile_filename(datafile),
-              afile_creator(rfile), afile_type(rfile));
-    if (afile_mode(datafile) != afile_mode(rfile))
-      fprintf(stderr, "afile:%s: mode does not match", afile_filename(datafile));
-    ret = RET_OK;
-  } else
-    ret = RET_INVALID_AD;
-
-  afile_delete(rfile);
-
-  return ret;
-}
-
-
-/* Parse a file and its resource fork. Output the file's creator/type. */
-static int parse_file(char *filename)
-{
-  int ret;
-  struct AFile *afile;
-
-  afile = afile_new(filename);
-  if (!afile) {
-    fprintf(stderr, "afile:%s: %s\n", filename, strerror(errno));
-    return errno == ENOENT ? RET_NO_SUCH_FILE : RET_UNREADABLE;
-  }
-
-  if (afile_is_dir(afile)) {
-    if (showall)
-      printf("directory %s\n", filename);
-    ret = RET_IS_DIR;
-  } else if (afile_is_ad(afile))
-    ret = handle_ad(afile);
-  else
-    ret = handle_datafile(afile);
-
-  afile_delete(afile);
-
-  return ret;
-}
-
-
-int main(int argc, char *argv[])
-{
-  int ret = RET_OK;
-
-  /* argument handling */
-  if (parse_args(argc, argv) == -1)
-    return RET_BAD_ARGS;
-
-  /* iterate through all files specified as arguments */
-  while (optind < argc)
-    ret = parse_file(argv[optind++]);
-
-  return ret;
-}
diff --git a/bin/afile/common.c b/bin/afile/common.c
deleted file mode 100644 (file)
index 6ec68a5..0000000
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * $Id: common.c,v 1.3 2001-06-29 14:14:46 rufustfirefly Exp $
- *
-    common functions, defines, and structures for afile, achfile, and acleandir
-
-    Copyright (C) 2001 Sebastian Rittau.
-    All rights reserved.
-
-    This file may be distributed and/or modfied under the terms of the
-    following license:
-
-    Redistribution and use in source and binary forms, with or without
-    modification, are permitted provided that the following conditions
-    are met:
-    1. Redistributions of source code must retain the above copyright
-       notice, this list of conditions and the following disclaimer.
-    2. Redistributions in binary form must reproduce the above copyright
-       notice, this list of conditions and the following disclaimer in the
-       documentation and/or other materials provided with the distribution.
-    3. Neither the name of the author nor the names of its contributors
-       may be used to endorse or promote products derived from this software
-       without specific prior written permission.
-
-    THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-    ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-    OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-    SUCH DAMAGE.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif /* HAVE_FCNTL_H */
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif /* HAVE_UNISTD_H */
-
-#include <atalk/adouble.h>
-#include <netatalk/endian.h>
-
-#include "common.h"
-
-
-#define AD_PREFIX ".AppleDouble/"
-#define PARENT_PREFIX "../"
-
-
-char *dataname_to_adname(const char *dataname)
-{
-  const char *filepart;
-  char *adname;
-  size_t adlen = strlen(AD_PREFIX);
-  size_t dirlen;
-
-  /* Construct the AppleDouble file name from data fork file name. */
-  adname = calloc(adlen + strlen(dataname) + 1, sizeof(char));
-  filepart = rindex(dataname, '/');
-  if (filepart == NULL) {
-    /* Filename doesn't contain a path. */
-    strcpy(adname, AD_PREFIX);
-    strcpy(adname + adlen, dataname);
-  } else {
-    /* Filename does contain a path. */
-    dirlen = (size_t) (filepart - dataname);
-    strncpy(adname, dataname, dirlen + 1);
-    strcpy(adname + dirlen + 1, AD_PREFIX);
-    strcpy(adname + dirlen + adlen + 1, filepart + 1);
-  }
-
-  return adname;
-}
-
-char *adname_to_dataname(const char *adname)
-{
-  const char *filepart;
-  char *dataname;
-  size_t plen = strlen(PARENT_PREFIX);
-  size_t dirlen;
-
-  /* Construct the data file name from the AppleDouble file name. */
-  dataname = calloc(strlen(adname) + plen + 1, sizeof(char));
-  filepart = rindex(adname, '/');
-  if (filepart == NULL) {
-    strcpy(dataname, PARENT_PREFIX);
-    strcpy(dataname + plen, adname);
-  } else {
-    dirlen = (size_t) (filepart - adname);
-    strncpy(dataname, adname, dirlen + 1);
-    strcpy(dataname + dirlen + 1, PARENT_PREFIX);
-    strcpy(dataname + dirlen + plen + 1, filepart + 1);
-  }
-
-  return dataname;
-}
-
-
-#define FLAG_NONE 0x0000
-#define FLAG_AD   0x0001
-#define FLAG_DIR  0x0002
-
-
-struct AFile *afile_new(const char *filename)
-{
-  struct stat fdstat;
-  char adbuf[AD_DATASZ];
-  ssize_t sz;
-  struct stat statbuf;
-
-  struct AFile *afile = (struct AFile *) calloc(sizeof(struct AFile), 1);
-  afile->filename     = filename;
-  afile->fd           = open(filename, O_RDONLY);
-  afile->flags        = FLAG_NONE;
-
-  /* Try to open file. */
-  if (afile->fd == -1) {
-    free(afile);
-    return NULL;
-  }
-
-  fstat(afile->fd, &statbuf);
-  afile->mode = statbuf.st_mode;
-
-  /* Try to determine if this file is a regular file, a directory, or
-   * something else.
-   */
-  fstat(afile->fd, &fdstat);
-  if (S_ISREG(fdstat.st_mode)) {                     /* it is a regular file */
-    /* Check if the opened file is the resource fork. */
-    sz = read(afile->fd, adbuf, AD_DATASZ);
-    if (sz >= 0) {
-      /* If we can't read a whole AppleDouble header, this can't be a valid
-       * AppleDouble file.
-       */
-      if (sz == AD_DATASZ) {
-       /* ... but as we could, maybe this is. Now if only the magic number
-        * would be correct ...
-        */
-       if (ntohl(((unsigned int *) adbuf)[0]) == AD_MAGIC)
-         /* Great! It obviously is a AppleDouble file. */
-         afile->flags |= FLAG_AD;
-         afile->creator[0] = adbuf[ADEDOFF_FINDERI + 0];
-         afile->creator[1] = adbuf[ADEDOFF_FINDERI + 1];
-         afile->creator[2] = adbuf[ADEDOFF_FINDERI + 2];
-         afile->creator[3] = adbuf[ADEDOFF_FINDERI + 3];
-         afile->type   [0] = adbuf[ADEDOFF_FINDERI + 4];
-         afile->type   [1] = adbuf[ADEDOFF_FINDERI + 5];
-         afile->type   [2] = adbuf[ADEDOFF_FINDERI + 6];
-         afile->type   [3] = adbuf[ADEDOFF_FINDERI + 7];
-      }
-    } else {
-      afile_delete(afile);
-      return NULL;
-    }
-    
-  } else if (S_ISDIR(fdstat.st_mode))                /* it is a directory */
-    afile->flags |= FLAG_DIR;
-  else {                                             /* it is neither */
-    afile_delete(afile);
-    return NULL;
-  }
-
-  return afile;
-}
-
-
-void afile_delete(struct AFile *afile)
-{
-  close(afile->fd);
-  free(afile);
-}
-
-
-const char *afile_filename(struct AFile *afile)
-{
-  return afile->filename;
-}
-
-int afile_is_ad(struct AFile *afile)
-{
-  return afile->flags & FLAG_AD;
-}
-
-int afile_is_dir(struct AFile *afile)
-{
-  return afile->flags & FLAG_DIR;
-}
-
-mode_t afile_mode(struct AFile *afile)
-{
-  return afile->mode;
-}
-
-const char *afile_creator(const struct AFile *afile)
-{
-  return afile->creator;
-}
-
-const char *afile_type(const struct AFile *afile)
-{
-  return afile->type;
-}
diff --git a/bin/afile/common.h b/bin/afile/common.h
deleted file mode 100644 (file)
index 8368909..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * $Id: common.h,v 1.2 2001-06-29 14:14:46 rufustfirefly Exp $
- *
-    common functions, defines, and structures for afile, achfile, and acleandir
-
-    Copyright (C) 2001 Sebastian Rittau.
-    All rights reserved.
-
-    This file may be distributed and/or modfied under the terms of the
-    following license:
-
-    Redistribution and use in source and binary forms, with or without
-    modification, are permitted provided that the following conditions
-    are met:
-    1. Redistributions of source code must retain the above copyright
-       notice, this list of conditions and the following disclaimer.
-    2. Redistributions in binary form must reproduce the above copyright
-       notice, this list of conditions and the following disclaimer in the
-       documentation and/or other materials provided with the distribution.
-    3. Neither the name of the author nor the names of its contributors
-       may be used to endorse or promote products derived from this software
-       without specific prior written permission.
-
-    THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-    ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-    OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-    SUCH DAMAGE.
-*/
-
-#include <stdlib.h>
-
-
-char *dataname_to_adname(const char *dataname);
-char *adname_to_dataname(const char *adname);
-
-/* Class for handling data and AppleDouble files. Don't access directly.
- * Instead, use the methods provided below.
- */
-struct AFile {
-  const char *filename;
-  int fd;
-  mode_t mode;
-  unsigned short flags;
-  char creator[4], type[4];
-};
-
-
-/* Constructor */
-struct AFile *afile_new(const char *filename);
-/* Destructor */
-void afile_delete(struct AFile *afile);
-/* Accessor Methods */
-const char *afile_filename(struct AFile *afile);
-int afile_is_ad(struct AFile *afile);
-int afile_is_dir(struct AFile *afile);
-mode_t afile_mode(struct AFile *afile);
-/* The following two methods are only valid if afile_is_ad yields true. */
-const char *afile_creator(const struct AFile *afile);
-const char *afile_type(const struct AFile *afile);
index 3aa59e933c9dc53f240a87ca9bbf3a91a6b2523a..2ade6f5dfa4ddefda2d13e1017364967619e59eb 100644 (file)
@@ -1,15 +1,7 @@
 # Makefile.am for bin/cnid/
 
 EXTRA_DIST = cnid2_create.in
-noinst_HEADERS = ad.h
 
 if USE_BDB
-bin_PROGRAMS = ad
 bin_SCRIPTS = cnid2_create
-
-ad_SOURCES = ad.c ad_util.c \
-       ad_ls.c \
-       ad_cp.c
-
-ad_LDADD = $(top_builddir)/libatalk/libatalk.la
 endif
diff --git a/bin/cnid/ad.c b/bin/cnid/ad.c
deleted file mode 100644 (file)
index 95faece..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/* 
-   $Id: ad.c,v 1.2 2009-10-13 22:55:36 didg Exp $
-
-   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
-   
-   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.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <limits.h>
-#include <signal.h>
-#include <string.h>
-#include <errno.h>
-
-#include <atalk/cnid.h>
-#include <atalk/volinfo.h>
-#include "ad.h"
-
-static void usage_main(void)
-{
-/*
-    printf("Usage: ad ls|rm|cp|mv|set [file|dir, ...]\n");
-*/
-    printf("Usage: ad ls [file|dir, ...]\n");
-}
-
-int main(int argc, char **argv)
-{
-    if (argc < 2) {
-        usage_main();
-        return 1;
-    }
-
-    if (STRCMP(argv[1], ==, "ls"))
-        return ad_ls(argc - 1, argv + 1);
-    else if (STRCMP(argv[1], ==, "cp"))
-        return ad_cp(argc - 1, argv + 1);
-    else {
-        usage_main();
-        return 1;
-    }
-
-    return 0;
-}
diff --git a/bin/cnid/ad.h b/bin/cnid/ad.h
deleted file mode 100644 (file)
index bb08085..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 
-   $Id: ad.h,v 1.1 2009-09-01 14:28:07 franklahm Exp $
-
-   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
-   
-   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.
-*/
-
-#ifndef AD_H
-#define AD_H
-
-#include <atalk/volinfo.h>
-
-#define STRCMP(a,b,c) (strcmp(a,c) b 0)
-
-#define DIR_DOT_OR_DOTDOT(a) \
-        ((strcmp(a, ".") == 0) || (strcmp(a, "..") == 0))
-
-typedef struct {
-    struct volinfo volinfo;
-//    char *dirname;
-//    char *basename;
-//    int adflags;                /* file:0, dir:ADFLAGS_DIR */
-} afpvol_t;
-
-
-extern int newvol(const char *path, afpvol_t *vol);
-extern void freevol(afpvol_t *vol);
-
-extern int ad_ls(int argc, char **argv);
-extern int ad_cp(int argc, char **argv);
-
-#endif /* AD_H */
diff --git a/bin/cnid/ad_cp.c b/bin/cnid/ad_cp.c
deleted file mode 100644 (file)
index ae15836..0000000
+++ /dev/null
@@ -1,303 +0,0 @@
-/* 
-   $Id: ad_cp.c,v 1.2 2009-10-13 22:55:36 didg Exp $
-
-   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
-   
-   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.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <limits.h>
-#include <string.h>
-#include <errno.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <pwd.h>
-#include <grp.h>
-#include <time.h>
-#include <libgen.h>
-
-#include <atalk/adouble.h>
-#include <atalk/cnid.h>
-#include <atalk/volinfo.h>
-#include <atalk/util.h>
-#include "ad.h"
-
-#define ADv2_DIRNAME ".AppleDouble"
-
-/* options */
-static int cp_R;
-static int cp_L;
-static int cp_P;
-static int cp_n;
-static int cp_p;
-static int cp_v;
-
-static char           *netatalk_dirs[] = {
-    ADv2_DIRNAME,
-    ".AppleDB",
-    ".AppleDesktop",
-    NULL
-};
-
-/*
-  Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
-  Returns pointer to name or NULL.
-*/
-static const char *check_netatalk_dirs(const char *name)
-{
-    int c;
-
-    for (c=0; netatalk_dirs[c]; c++) {
-        if ((strcmp(name, netatalk_dirs[c])) == 0)
-            return netatalk_dirs[c];
-    }
-    return NULL;
-}
-
-
-static void usage_cp(void)
-{
-    printf(
-        "Usage: ad cp [-R [-L | -P]] [-pv] <source_file> <target_file>\n"
-        "Usage: ad cp [-R [-L | -P]] [-pv] <source_file [source_file ...]> <target_directory>\n"
-        );
-}
-
-static int ad_cp_copy(const afpvol_t *srcvol, const afpvol_t *dstvol,
-                      char *srcfile, char *dstfile, struct stat *st)
-{
-    printf("copy: '%s' -> '%s'\n", srcfile, dstfile);
-    return 0;
-}
-
-static int ad_cp_r(const afpvol_t *srcvol, const afpvol_t *dstvol, char *srcdir, char *dstdir)
-{
-    int ret = 0, cwd, dirprinted = 0, dirempty;
-    static char srcpath[MAXPATHLEN+1];
-    static char dstpath[MAXPATHLEN+1];
-    char *tmp;
-    DIR *dp;
-    struct dirent *ep;
-    static struct stat st;      /* Save some stack space */
-
-    strlcat(srcpath, srcdir, sizeof(srcpath));
-    strlcat(dstpath, dstdir, sizeof(dstpath));
-
-    if ((dp = opendir (srcdir)) == NULL) {
-        perror("Couldn't opendir .");
-        return -1;
-    }
-
-    /* First run: copy files */
-    while ((ep = readdir (dp))) {
-        /* Check if its "." or ".." */
-        if (DIR_DOT_OR_DOTDOT(ep->d_name))
-            continue;
-
-        /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
-        if ((check_netatalk_dirs(ep->d_name)) != NULL)
-            continue;
-
-        if (lstat(ep->d_name, &st) < 0) {
-            perror("Can't stat");
-            return -1;
-        }
-
-        /* Build paths, copy, strip name */
-        strlcat(srcpath, "/", sizeof(srcpath));
-        strlcat(dstpath, "/", sizeof(dstpath));
-        strlcat(srcpath, ep->d_name, sizeof(srcpath));
-        strlcat(dstpath, ep->d_name, sizeof(dstpath));
-
-        ret = ad_cp_copy(srcvol, dstvol, srcpath, dstpath, &st);
-
-        if ((tmp = strrchr(srcpath, '/')))
-            *tmp = 0;
-        if ((tmp = strrchr(dstpath, '/')))
-            *tmp = 0;
-
-        if (ret != 0)
-            goto exit;
-    }
-
-    /* Second run: recurse to dirs */
-    rewinddir(dp);
-    while ((ep = readdir (dp))) {
-        /* Check if its "." or ".." */
-        if (DIR_DOT_OR_DOTDOT(ep->d_name))
-            continue;
-        
-        /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
-        if (check_netatalk_dirs(ep->d_name) != NULL)
-            continue;
-        
-        if (lstat(ep->d_name, &st) < 0) {
-            perror("Can't stat");
-            return -1;
-        }
-        
-        /* Recursion */
-        if (S_ISDIR(st.st_mode)) {
-            strlcat(srcpath, "/", sizeof(srcpath));
-            strlcat(dstpath, "/", sizeof(dstpath));
-            ret = ad_cp_r(srcvol, dstvol, ep->d_name, ep->d_name);
-        }
-        if (ret != 0)
-            goto exit;
-    }
-
-exit:
-    closedir(dp);
-    fchdir(cwd);
-    close(cwd);
-
-    if ((tmp = strrchr(srcpath, '/')))
-        *tmp = 0;
-    if ((tmp = strrchr(dstpath, '/')))
-        *tmp = 0;
-
-    return ret;
-}
-
-int ad_cp(int argc, char **argv)
-{
-    int c, numpaths;
-    afpvol_t srcvvol;
-    struct stat sst;
-    struct stat dst;
-    afpvol_t srcvol;
-    afpvol_t dstvol;
-    char *srcfile = NULL;
-    char *srcdir = NULL;    
-    char *dstfile = NULL;
-    char *dstdir = NULL;
-    char path[MAXPATHLEN+1];
-    char *basenametmp;
-
-    while ((c = getopt(argc, argv, ":npvLPR")) != -1) {
-        switch(c) {
-        case 'n':
-            cp_n = 1;
-            break;
-        case 'p':
-            cp_p = 1;
-            break;
-        case 'v':
-            cp_v = 1;
-            break;
-        case 'L':
-            cp_L = 1;
-            break;
-        case 'P':
-            cp_P = 1;
-            break;
-        case 'R':
-            cp_R = 1;
-            break;
-        case ':':
-        case '?':
-            usage_cp();
-            return -1;
-            break;
-        }
-    }
-
-    /* How many pathnames do we have */
-    numpaths = argc - optind;
-    printf("Number of paths: %u\n", numpaths);
-    if (numpaths < 2) {
-        usage_cp();
-        exit(EXIT_FAILURE);
-    }
-
-    while ( argv[argc-1][(strlen(argv[argc-1]) - 1)] == '/')
-        argv[argc-1][(strlen(argv[argc-1]) - 1)] = 0;
-    printf("Destination is: %s\n", argv[argc-1]);
-
-    /* Create vol for destination */
-    newvol(argv[argc-1], &dstvol);
-
-    if (numpaths == 2) {
-        /* Case 1: 2 paths */
-        if (stat(argv[optind], &sst) != 0)
-            goto next;
-        if (S_ISREG(sst.st_mode)) {
-            /* Either file to file or file to dir copy */
-            newvol(argv[optind], &srcvol);
-            ad_cp_copy(&srcvol, &dstvol, srcfile, path, &sst);
-            freevol(&srcvol);
-            freevol(&dstvol);
-            
-        } else if (S_ISDIR(sst.st_mode)) {
-            /* dir to dir copy. Check if -R is requested */
-            if (!cp_R) {
-                usage_cp();
-                exit(EXIT_FAILURE);
-            }
-        }
-
-    } else {
-        /* Case 2: >2 paths */
-        while (optind < (argc-1)) {
-            printf("Source is: %s\n", argv[optind]);
-            newvol(argv[optind], &srcvol);
-            if (stat(argv[optind], &sst) != 0)
-                goto next;
-            if (S_ISDIR(sst.st_mode)) {
-                /* Source is a directory */
-                if (!cp_R) {
-                    printf("Source %s is a directory\n", argv[optind]);
-                    goto next;
-                }
-                srcdir = argv[optind];
-                dstdir = argv[argc-1];
-                
-                ad_cp_r(&srcvol, &dstvol, srcdir, dstdir);
-                freevol(&srcvol);
-            } else {
-                /* Source is a file */
-                srcfile = argv[optind];
-                if (numpaths != 2 && !dstdir) {
-                    usage_cp();
-                    exit(EXIT_FAILURE);
-                }
-                path[0] = 0;
-                strlcat(path, dstdir, sizeof(path));
-                basenametmp = strdup(srcfile);
-                strlcat(path, basename(basenametmp), sizeof(path));
-                free(basenametmp);
-                printf("%s %s\n", srcfile, path);
-                if (ad_cp_copy(&srcvol, &dstvol, srcfile, path, &sst) != 0) {
-                    freevol(&srcvol);
-                    freevol(&dstvol);
-                    exit(EXIT_FAILURE);
-                }
-            } /* else */
-        next:
-            optind++;
-        } /* while */
-    } /* else (numpath>2) */
-
-    freevol(&dstvol);
-
-    return 0;
-
-}
diff --git a/bin/cnid/ad_ls.c b/bin/cnid/ad_ls.c
deleted file mode 100644 (file)
index 0708c77..0000000
+++ /dev/null
@@ -1,637 +0,0 @@
-/* 
-   $Id: ad_ls.c,v 1.4 2009-10-14 01:38:28 didg Exp $
-
-   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
-   
-   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.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <limits.h>
-#include <string.h>
-#include <errno.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <pwd.h>
-#include <grp.h>
-#include <time.h>
-
-#include <atalk/adouble.h>
-#include <atalk/cnid.h>
-#include <atalk/volinfo.h>
-#include "ad.h"
-
-#define ADv2_DIRNAME ".AppleDouble"
-
-#define DIR_DOT_OR_DOTDOT(a) \
-        ((strcmp(a, ".") == 0) || (strcmp(a, "..") == 0))
-
-/* ls options */
-static int ls_a;
-static int ls_l;
-static int ls_R;
-static int ls_d;
-static int ls_u;
-
-/* Used for pretty printing */
-static int first = 1;
-static int recursion;
-
-static char           *netatalk_dirs[] = {
-    ADv2_DIRNAME,
-    ".AppleDB",
-    ".AppleDesktop",
-    NULL
-};
-
-static char *labels[] = {
-    "---",
-    "gry",
-    "gre",
-    "vio",
-    "blu",
-    "yel",
-    "red",
-    "ora"
-};
-
-/*
-  Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
-  Returns pointer to name or NULL.
-*/
-static const char *check_netatalk_dirs(const char *name)
-{
-    int c;
-
-    for (c=0; netatalk_dirs[c]; c++) {
-        if ((strcmp(name, netatalk_dirs[c])) == 0)
-            return netatalk_dirs[c];
-    }
-    return NULL;
-}
-
-
-static void usage_ls(void)
-{
-    printf(
-        "Usage: ad ls [-dRl[u]] [file|dir, ...]\n\n"
-        "  -l Long Output [-u: unix info]:\n"
-        "     <unixinfo ...> <FinderFlags> <AFPAttributes> <Color> <Type> <Creator> <CNID from AppleDouble> <name>\n\n"
-        "     FinderFlags (valid for (f)ile and/or (d)irectory):\n"
-        "       d = On Desktop (f/d)\n"
-        "       e = Hidden extension (f/d)\n"
-        "       m = Shared (can run multiple times) (f)\n"
-        "       n = No INIT resources (f)\n"
-        "       i = Inited (f/d)\n"
-        "       c = Custom icon (f/d)\n"
-        "       t = Stationery (f)\n"
-        "       s = Name locked (f/d)\n"
-        "       b = Bundle (f/d)\n"
-        "       v = Invisible (f/d)\n"
-        "       a = Alias file (f/d)\n\n"
-        "     AFPAttributes:\n"
-        "       y = System (f/d)\n"
-        "       w = No write (f)\n"
-        "       p = Needs backup (f/d)\n"
-        "       r = No rename (f/d)\n"
-        "       l = No delete (f/d)\n"
-        "       o = No copy (f)\n\n"
-        "     Note: any letter appearing in uppercase means the flag is set\n"
-        "           but it's a directory for which the flag is not allowed.\n"
-        );
-}
-
-static void print_numlinks(const struct stat *statp)
-{
-    printf("%5ld", (long)statp->st_nlink);
-}
-
-static void print_owner(const struct stat *statp)
-{
-    struct passwd *pwd = getpwuid(statp->st_uid);
-
-    if (pwd == NULL)
-        printf(" %-8ld", (long)statp->st_uid);
-    else
-        printf(" %-8s", pwd->pw_name);
-}
-
-static void print_group(const struct stat *statp)
-{
-    struct group *grp = getgrgid(statp->st_gid);
-
-    if (grp == NULL)
-        printf(" %-8ld", (long)statp->st_gid);
-    else
-        printf(" %-8s", grp->gr_name);
-}
-
-static void print_size(const struct stat *statp)
-{
-    switch (statp->st_mode & S_IFMT) {
-    case S_IFCHR:
-    case S_IFBLK:
-        printf("%4u,%4u", (unsigned)(statp->st_rdev >> 8),
-               (unsigned)(statp->st_rdev & 0xFF));
-        break;
-    default:
-        printf("%9lu", (unsigned long)statp->st_size);
-    }
-}
-
-static void print_date(const struct stat *statp)
-{
-    time_t now;
-    double diff;
-    char buf[100], *fmt;
-
-    if (time(&now) == -1) {
-        printf(" ????????????");
-        return;
-    }
-    diff = difftime(now, statp->st_mtime);
-    if (diff < 0 || diff > 60 * 60 * 24 * 182.5)
-        fmt = "%b %e  %Y";
-    else
-        fmt = "%b %e %H:%M";
-    strftime(buf, sizeof(buf), fmt, localtime(&statp->st_mtime));
-    printf(" %s", buf);
-}
-
-static void print_flags(char *path, afpvol_t *vol, const struct stat *st)
-{
-    int adflags = 0;
-    struct adouble ad;
-    char *FinderInfo;
-    uint16_t FinderFlags;
-    uint16_t AFPattributes;
-    char type[5] = "----";
-    char creator[5] = "----";
-    int i;
-    uint32_t cnid;
-
-    if (S_ISDIR(st->st_mode))
-        adflags = ADFLAGS_DIR;
-
-    if (vol->volinfo.v_path == NULL)
-        return;
-
-    ad_init(&ad, vol->volinfo.v_adouble, vol->volinfo.v_ad_options);
-
-    if ( ad_metadata(path, adflags, &ad) < 0 )
-        return;
-
-    FinderInfo = ad_entry(&ad, ADEID_FINDERI);
-
-    memcpy(&FinderFlags, FinderInfo + 8, 2);
-    FinderFlags = ntohs(FinderFlags);
-
-    memcpy(type, FinderInfo, 4);
-    memcpy(creator, FinderInfo + 4, 4);
-
-    ad_getattr(&ad, &AFPattributes);
-    AFPattributes = ntohs(AFPattributes);
-
-    /*
-      Finder flags. Lowercase means valid, uppercase means invalid because
-      object is a dir and flag is only valid for files.
-    */
-    putchar(' ');
-    if (FinderFlags & FINDERINFO_ISONDESK)
-        putchar('d');
-    else
-        putchar('-');
-
-    if (FinderFlags & FINDERINFO_HIDEEXT)
-        putchar('e');
-    else
-        putchar('-');
-
-    if (FinderFlags & FINDERINFO_ISHARED) {
-        if (adflags & ADFLAGS_DIR)
-            putchar('M');
-        else
-            putchar('m');
-    } else
-        putchar('-');
-
-    if (FinderFlags & FINDERINFO_HASNOINITS) {
-        if (adflags & ADFLAGS_DIR)
-            putchar('N');
-        else
-            putchar('n');
-    } else
-        putchar('-');
-
-    if (FinderFlags & FINDERINFO_HASBEENINITED)
-        putchar('i');
-    else
-        putchar('-');
-
-    if (FinderFlags & FINDERINFO_HASCUSTOMICON)
-        putchar('c');
-    else
-        putchar('-');
-
-    if (FinderFlags & FINDERINFO_ISSTATIONNERY) {
-        if (adflags & ADFLAGS_DIR)
-            putchar('T');
-        else
-            putchar('t');
-    } else
-        putchar('-');
-
-    if (FinderFlags & FINDERINFO_NAMELOCKED)
-        putchar('s');
-    else
-        putchar('-');
-
-    if (FinderFlags & FINDERINFO_HASBUNDLE)
-        putchar('b');
-    else
-        putchar('-');
-
-    if (FinderFlags & FINDERINFO_INVISIBLE)
-        putchar('v');
-    else
-        putchar('-');
-
-    if (FinderFlags & FINDERINFO_ISALIAS)
-        putchar('a');
-    else
-        putchar('-');
-
-    putchar(' ');
-
-    /* AFP attributes */
-    if (AFPattributes & ATTRBIT_SYSTEM)
-        putchar('y');
-    else
-        putchar('-');
-
-    if (AFPattributes & ATTRBIT_NOWRITE) {
-        if (adflags & ADFLAGS_DIR)
-            putchar('W');
-        else
-            putchar('w');
-    } else
-        putchar('-');
-
-    if (AFPattributes & ATTRBIT_BACKUP)
-        putchar('p');
-    else
-        putchar('-');
-
-    if (AFPattributes & ATTRBIT_NORENAME)
-        putchar('r');
-    else
-        putchar('-');
-
-    if (AFPattributes & ATTRBIT_NODELETE)
-        putchar('l');
-    else
-        putchar('-');
-
-    if (AFPattributes & ATTRBIT_NOCOPY) {
-        if (adflags & ADFLAGS_DIR)
-            putchar('O');
-        else
-            putchar('o');                
-    } else
-        putchar('-');
-
-    /* Color */
-    printf(" %s ", labels[(FinderFlags & FINDERINFO_COLOR) >> 1]);
-
-    /* Type & Creator */
-    for(i=0; i<4; i++) {
-        if (isalnum(type[i]))
-            putchar(type[i]);
-        else
-            putchar('-');
-    }
-    putchar(' '); 
-    for(i=0; i<4; i++) {
-        if (isalnum(creator[i]))
-            putchar(creator[i]);
-        else
-            putchar('-');
-    }
-    putchar(' '); 
-
-    /* CNID */
-    cnid = ad_forcegetid(&ad);
-    if (cnid)
-        printf(" %10u ", ntohl(cnid));
-    else
-        printf(" !ADVOL_CACHE ");
-
-    ad_close_metadata(&ad);
-}
-
-#define TYPE(b) ((st->st_mode & (S_IFMT)) == (b))
-#define MODE(b) ((st->st_mode & (b)) == (b))
-
-static void print_mode(const struct stat *st)
-{
-    if (TYPE(S_IFBLK))
-        putchar('b');
-    else if (TYPE(S_IFCHR))
-        putchar('c');
-    else if (TYPE(S_IFDIR))
-        putchar('d');
-    else if (TYPE(S_IFIFO))
-        putchar('p');
-    else if (TYPE(S_IFREG))
-        putchar('-');
-    else if (TYPE(S_IFLNK))
-        putchar('l');
-    else if (TYPE(S_IFSOCK))
-        putchar('s');
-    else
-        putchar('?');
-    putchar(MODE(S_IRUSR) ? 'r' : '-');
-    putchar(MODE(S_IWUSR) ? 'w' : '-');
-    if (MODE(S_ISUID)) {
-        if (MODE(S_IXUSR))
-            putchar('s');
-        else
-            putchar('S');
-    }
-    else if (MODE(S_IXUSR))
-        putchar('x');
-    else
-        putchar('-');
-    putchar(MODE(S_IRGRP) ? 'r' : '-');
-    putchar(MODE(S_IWGRP) ? 'w' : '-');
-    if (MODE(S_ISGID)) {
-        if (MODE(S_IXGRP))
-            putchar('s');
-        else
-            putchar('S');
-    }
-    else if (MODE(S_IXGRP))
-        putchar('x');
-    else
-        putchar('-');
-    putchar(MODE(S_IROTH) ? 'r' : '-');
-    putchar(MODE(S_IWOTH) ? 'w' : '-');
-    if (MODE(S_IFDIR) && MODE(S_ISVTX)) {
-        if (MODE(S_IXOTH))
-            putchar('t');
-        else
-            putchar('T');
-    }
-    else if (MODE(S_IXOTH))
-        putchar('x');
-    else
-        putchar('-');
-}
-#undef TYPE
-#undef MODE
-
-static int ad_print(char *path, const struct stat *st, afpvol_t *vol)
-{
-    if ( ! ls_l) {
-        printf("%s  ", path);
-        if (ls_d)
-            printf("\n");
-        return 0;
-    }
-
-    /* Long output */
-    if (ls_u) {
-        print_mode(st);
-        print_numlinks(st);
-        print_owner(st);
-        print_group(st);
-        print_size(st);
-        print_date(st);
-    }
-    print_flags(path, vol, st);
-    printf("  %s\n", path);    
-
-
-    return 0;
-}
-
-static int ad_ls_r(char *path, afpvol_t *vol)
-{
-    int ret = 0, cwd, dirprinted = 0, dirempty;
-    const char *name;
-    char *tmp;
-    static char cwdpath[MAXPATHLEN+1];
-    DIR *dp;
-    struct dirent *ep;
-    static struct stat st;      /* Save some stack space */
-
-    if ( first)
-        cwdpath[0] = 0;
-    else
-        strcat(cwdpath, "/");
-
-    strcat(cwdpath, path);
-    first = 0;
-
-    if (lstat(path, &st) < 0) {
-        perror("Can't stat");
-        return -1;
-    }
-    /* If its a file or a dir with -d option call ad_print and return */
-    if (S_ISREG(st.st_mode) || ls_d)
-        return ad_print(path, &st, vol);
-
-    /* Its a dir: chdir to it remembering where we started */
-    if ((cwd = open(".", O_RDONLY)) == -1) {
-        perror("Cant open .");
-        return -1;
-    }
-    if (chdir(path) != 0) {
-        perror("Cant chdir");
-        close(cwd);
-        return -1;
-    }
-
-    if ((dp = opendir (".")) == NULL) {
-        perror("Couldn't opendir .");
-        return -1;
-    }
-
-    /* First run: print everything */
-    dirempty = 1;
-    while ((ep = readdir (dp))) {
-        /* Check if its "." or ".." */
-        if (DIR_DOT_OR_DOTDOT(ep->d_name))
-            continue;
-
-        /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
-        if ((name = check_netatalk_dirs(ep->d_name)) != NULL)
-            continue;
-
-        if ((ep->d_name[0] == '.') && ! ls_a)
-            continue;
-
-        dirempty = 0;
-
-        if (recursion && ! dirprinted) {
-            printf("\n%s:\n", cwdpath);
-            dirprinted = 1;
-        }
-
-        if (lstat(ep->d_name, &st) < 0) {
-            perror("Can't stat");
-            return -1;
-        }
-
-        ret = ad_print(ep->d_name, &st, vol);
-        if (ret != 0)
-            goto exit;
-    }
-
-    if (! ls_l && ! dirempty)
-        printf("\n");
-
-    /* Second run: recurse to dirs */
-    if (ls_R) {
-        rewinddir(dp);
-        while ((ep = readdir (dp))) {
-            /* Check if its "." or ".." */
-            if (DIR_DOT_OR_DOTDOT(ep->d_name))
-                continue;
-
-            /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
-            if ((name = check_netatalk_dirs(ep->d_name)) != NULL)
-                continue;
-
-            if (lstat(ep->d_name, &st) < 0) {
-                perror("Can't stat");
-                return -1;
-            }
-
-            /* Recursion */
-            if (S_ISDIR(st.st_mode)) {
-                recursion = 1;
-                ret = ad_ls_r(ep->d_name, vol);
-            }
-            if (ret != 0)
-                goto exit;
-        }
-    }
-
-exit:
-    closedir(dp);
-    fchdir(cwd);
-    close(cwd);
-
-    tmp = strrchr(cwdpath, '/');
-    if (tmp)
-        *tmp = 0;
-
-    return ret;
-}
-
-int ad_ls(int argc, char **argv)
-{
-    int c, firstarg;
-    afpvol_t vol;
-    struct stat st;
-
-    while ((c = getopt(argc, argv, ":adlRu")) != -1) {
-        switch(c) {
-        case 'a':
-            ls_a = 1;
-            break;
-        case 'd':
-            ls_d = 1;
-            break;
-        case 'l':
-            ls_l = 1;
-            break;
-        case 'R':
-            ls_R = 1;
-            break;
-        case 'u':
-            ls_u = 1;
-            break;
-        case ':':
-        case '?':
-            usage_ls();
-            return -1;
-            break;
-        }
-
-    }
-
-    if ((argc - optind) == 0) {
-        newvol(".", &vol);
-        ad_ls_r(".", &vol);
-        freevol(&vol);
-    }
-    else {
-        int havefile = 0;
-
-        firstarg = optind;
-
-        /* First run: only print files from argv paths */
-        while(optind < argc) {
-            if (stat(argv[optind], &st) != 0)
-                goto next;
-            if (S_ISDIR(st.st_mode))
-                goto next;
-
-            havefile = 1;
-            first = 1;
-            recursion = 0;
-
-            newvol(argv[optind], &vol);
-            ad_ls_r(argv[optind], &vol);
-            freevol(&vol);
-        next:
-            optind++;
-        }
-        if (havefile && (! ls_l))
-            printf("\n");
-
-        /* Second run: print dirs */
-        optind = firstarg;
-        while(optind < argc) {
-            if (stat(argv[optind], &st) != 0)
-                goto next2;
-            if ( ! S_ISDIR(st.st_mode))
-                goto next2;
-            if ((optind > firstarg) || havefile)
-                printf("\n%s:\n", argv[optind]);
-
-            first = 1;
-            recursion = 0;
-
-            newvol(argv[optind], &vol);
-            ad_ls_r(argv[optind], &vol);
-            freevol(&vol);
-
-        next2:
-            optind++;
-        }
-
-
-    }
-
-    return 0;
-}
diff --git a/bin/cnid/ad_util.c b/bin/cnid/ad_util.c
deleted file mode 100644 (file)
index 339a72b..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/* 
-   $Id: ad_util.c,v 1.2 2009-10-14 02:30:42 didg Exp $
-
-   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
-   
-   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.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-#include <errno.h>
-#include <libgen.h>
-
-#include <atalk/cnid.h>
-#include <atalk/volinfo.h>
-#include "ad.h"
-
-
-int newvol(const char *path, afpvol_t *vol)
-{
-//    char *pathdup;
-
-    memset(vol, 0, sizeof(afpvol_t));
-
-//    pathdup = strdup(path);
-//    vol->dirname = strdup(dirname(pathdup));
-//    free(pathdup);
-
-//    pathdup = strdup(path);
-//    vol->basename = strdup(basename(pathdup));
-//    free(pathdup);
-
-    loadvolinfo((char *)path, &vol->volinfo);
-
-    return 0;
-}
-
-void freevol(afpvol_t *vol)
-{
-#if 0
-    if (vol->dirname) {
-        free(vol->dirname);
-        vol->dirname = NULL;
-    }
-    if (vol->basename) {
-        free(vol->basename);
-        vol->basename = NULL;
-    }
-#endif
-}
index 60c754f0ac8fae5a6acd4679c806b8e6f7352007..467ac5f7eb302ab576a70b8b7dafe5716eeb61f2 100644 (file)
@@ -118,7 +118,7 @@ int single_header_read( struct FHeader *fh, int version)
  * entry_buf is used for reading in entry descriptors, and for reading in
  *     the actual entries of FILEINFO, FINDERINFO, and DATES.
  */
-    u_char             entry_buf[ 16 ];
+    u_char             entry_buf[ADEDLEN_FINDERI];
     u_int32_t          entry_id;
     u_int32_t          time_seconds;
     u_short            mask = 0xfcee;
@@ -204,15 +204,14 @@ int single_header_read( struct FHeader *fh, int version)
            return( -1 );
        }
     }
-    if (( single.entry[ ADEID_FINDERI ].ade_len < 16 ) ||
+    if (( single.entry[ ADEID_FINDERI ].ade_len < ADEDLEN_FINDERI ) ||
            ( single.entry[ ADEID_FINDERI ].ade_off <= AD_HEADER_LEN )) {
        fprintf( stderr, "%s has bogus FinderInfo.\n", single.path );
        return( -1 );
     } else {
        pos = lseek( single.filed,
                single.entry[ ADEID_FINDERI ].ade_off, SEEK_SET );
-       if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
-               sizeof( entry_buf )) {
+       if ( read( single.filed, (char *)entry_buf, ADEDLEN_FINDERI) != ADEDLEN_FINDERI) {
            perror( "Premature end of file :" );
            return( -1 );
        }
index 349a7e1efc8725875dd4503d0bd3a46656ccb496..73ae629204b4ff0afd55b417a03e72cbe61062ef 100644 (file)
@@ -239,7 +239,7 @@ ssize_t bin_read( int fork, char *buffer, size_t length)
 ssize_t bin_write(int fork, char *buffer, size_t length)
 {
     char               *buf_ptr;
-    size_t             writelen;
+    ssize_t            writelen;
     ssize_t            cc = 0;
     off_t              pos;
     u_char             padchar = 0x7f;
@@ -523,7 +523,7 @@ int bin_header_write(struct FHeader *fh)
 int test_header(void)
 {
     const char          zeros[25] = "";
-    u_int32_t          cc;
+    ssize_t            cc;
     u_short            header_crc;
     u_char             namelen;
 
index 15f4cd1a3254accb1b4e6365a1ff16f311e6e708..2bff10ed3a1d944593ad9ff8cea65d8cbecbe84d 100644 (file)
@@ -1,9 +1,10 @@
 Makefile
 Makefile.in
 netacnv
+afpldaptest
+logger_test
 .deps
 .libs
 *.o
-afpldaptest
-.gitignore
-netacnv.o
+
+
index 9b55ee3621fe40a12364623c764b4e387fa1bd93..04a53e23e8b72fa1c59ff0af9944890f9a6188af 100644 (file)
@@ -1,16 +1,18 @@
 # Makefile.am for bin/misc
 
-bin_PROGRAMS = netacnv
+pkgconfdir = @PKGCONFDIR@
+bin_PROGRAMS =
+
+noinst_PROGRAMS = netacnv logger_test
 
 netacnv_SOURCES = netacnv.c
 netacnv_LDADD = $(top_builddir)/libatalk/libatalk.la
-pkgconfdir = @PKGCONFDIR@
 
-if USE_NFSv4_ACLS
-bin_PROGRAMS += afpldaptest
+logger_test_SOURCES = logger_test.c
+logger_test_LDADD = $(top_builddir)/libatalk/libatalk.la
 
+bin_PROGRAMS += afpldaptest
 afpldaptest_SOURCES = uuidtest.c
+afpldaptest_CFLAGS = -D_PATH_ACL_LDAPCONF=\"$(pkgconfdir)/afp_ldap.conf\"
 afpldaptest_LDADD =  $(top_builddir)/libatalk/libatalk.la
 
-AM_CFLAGS = -D_PATH_ACL_LDAPCONF=\"$(pkgconfdir)/afp_ldap.conf\"
-endif
diff --git a/bin/misc/logger_test.c b/bin/misc/logger_test.c
new file mode 100644 (file)
index 0000000..a968cce
--- /dev/null
@@ -0,0 +1,94 @@
+#include <stdio.h>
+
+#include <atalk/boolean.h>
+#include <atalk/logger.h>
+
+int main(int argc, char *argv[])
+{
+  set_processname("logger_Test");
+#if 0
+  LOG(log_severe, logtype_logger, "Logging Test starting: this should only log to syslog");
+
+  /* syslog testing */
+  LOG(log_severe, logtype_logger, "Disabling syslog logging.");
+  unsetuplog("Default");
+  LOG(log_error, logtype_default, "This shouldn't log to syslog: LOG(log_error, logtype_default).");
+  LOG(log_error, logtype_logger, "This shouldn't log to syslog: LOG(log_error, logtype_logger).");
+  setuplog("Default LOG_INFO");
+  LOG(log_info, logtype_logger, "Set syslog logging to 'log_info', so this should log again. LOG(log_info, logtype_logger).");
+  LOG(log_error, logtype_logger, "This should log to syslog: LOG(log_error, logtype_logger).");
+  LOG(log_error, logtype_default, "This should log to syslog. LOG(log_error, logtype_default).");
+  LOG(log_debug, logtype_logger, "This shouldn't log to syslog. LOG(log_debug, logtype_logger).");
+  LOG(log_debug, logtype_default, "This shouldn't log to syslog. LOG(log_debug, logtype_default).");
+  LOG(log_severe, logtype_logger, "Disabling syslog logging.");
+  unsetuplog("Default");
+#endif
+  /* filelog testing */
+
+  setuplog("DSI log_maxdebug test.log");
+  LOG(log_info, logtype_dsi, "This should log.");
+  LOG(log_error, logtype_default, "This should not log.");
+
+  setuplog("Default log_debug test.log");
+  LOG(log_debug, logtype_default, "This should log.");
+  LOG(log_maxdebug, logtype_default, "This should not log.");
+
+  LOG(log_maxdebug, logtype_dsi, "This should still log.");
+
+  /* flooding prevention check */
+  LOG(log_debug, logtype_default, "Flooding 3x");
+  for (int i = 0; i < 3; i++) {
+      LOG(log_debug, logtype_default, "Flooding...");
+  }
+  /* wipe the array */
+  LOG(log_debug, logtype_default, "1"); LOG(log_debug, logtype_default, "2"); LOG(log_debug, logtype_default, "3");
+
+  LOG(log_debug, logtype_default, "-============");
+  LOG(log_debug, logtype_default, "Flooding 5x");
+  for (int i = 0; i < 5; i++) {
+      LOG(log_debug, logtype_default, "Flooding...");
+  }
+  LOG(log_debug, logtype_default, "1"); LOG(log_debug, logtype_default, "2"); LOG(log_debug, logtype_default, "3");
+
+  LOG(log_debug, logtype_default, "o============");
+  LOG(log_debug, logtype_default, "Flooding 2005x");
+  for (int i = 0; i < 2005; i++) {
+      LOG(log_debug, logtype_default, "Flooding...");
+  }
+  LOG(log_debug, logtype_default, "1"); LOG(log_debug, logtype_default, "2"); LOG(log_debug, logtype_default, "3");
+
+  LOG(log_debug, logtype_default, "0============");
+  LOG(log_debug, logtype_default, "Flooding 11x1");
+  for (int i = 0; i < 11; i++) {
+      LOG(log_error, logtype_default, "flooding 11x1 1");
+  }
+
+  LOG(log_debug, logtype_default, "1============");
+  LOG(log_debug, logtype_default, "Flooding 11x2");
+  for (int i = 0; i < 11; i++) {
+      LOG(log_error, logtype_default, "flooding 11x2 1");
+      LOG(log_error, logtype_default, "flooding 11x2 2");
+  }
+
+  LOG(log_debug, logtype_default, "2============");
+  LOG(log_debug, logtype_default, "Flooding 11x3");
+  for (int i = 0; i < 11; i++) {
+      LOG(log_error, logtype_default, "flooding 11x3 1");
+      LOG(log_error, logtype_default, "flooding 11x3 2");
+      LOG(log_error, logtype_default, "flooding 11x3 3");
+  }
+
+  LOG(log_debug, logtype_default, "3============");
+  LOG(log_debug, logtype_default, "Flooding 11x4");
+  for (int i = 0; i < 11; i++) {
+      LOG(log_error, logtype_default, "flooding 11x4 1");
+      LOG(log_error, logtype_default, "flooding 11x4 2");
+      LOG(log_error, logtype_default, "flooding 11x4 3");
+      LOG(log_error, logtype_default, "flooding 11x4 4");
+  }
+
+
+  return 0;
+}
+
+
index a4c700c5c9814ccdb0740180329854fa9bff72cb..b12585ee9b7e57a2a7e57fdff81330d7e3de218f 100644 (file)
@@ -1,5 +1,4 @@
 /*
-  $Id: uuidtest.c,v 1.3 2009-11-28 12:27:24 franklahm Exp $
   Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
 
   This program is free software; you can redistribute it and/or modify
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
 
-#ifdef HAVE_NFSv4_ACLS
-
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdarg.h>
+
+#ifdef HAVE_LDAP
+#define LDAP_DEPRECATED 1
 #include <ldap.h>
+#endif
 
 #include <atalk/ldapconfig.h>
 #include <atalk/uuid.h>
@@ -42,6 +43,7 @@ static void parse_ldapconf()
     static int inited = 0;
 
     if (! inited) {
+#ifdef HAVE_LDAP
         /* Parse afp_ldap.conf */
         printf("Start parsing afp_ldap.conf\n");
         acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
@@ -57,70 +59,80 @@ static void parse_ldapconf()
                 exit(EXIT_FAILURE);
             }
         } else {
-            printf("afp_ldap.conf is not ok.\n");
-            exit(EXIT_FAILURE);
+            printf("afp_ldap.conf is not ok, not using LDAP. Only local UUID testing available.\n");
         }
+#else
+        printf("Built without LDAP support, only local UUID testing available.\n");
+#endif
         inited = 1;
     }
 }
 
 int main( int argc, char **argv)
 {
-    int ret, i, c;
+    int ret, c;
     int verbose = 0;
-    uuid_t uuid;
+    atalk_uuid_t uuid;
+    int logsetup = 0;
     uuidtype_t type;
-    char *uuidstring = NULL;
     char *name = NULL;
 
-    setuplog("console log_error /dev/tty");
-
     while ((c = getopt(argc, argv, ":vu:g:i:")) != -1) {
         switch(c) {
 
         case 'v':
             if (! verbose) {
                 verbose = 1;
-                setuplog("console log_debug /dev/tty");
+                setuplog("default log_maxdebug /dev/tty");
+                logsetup = 1;
             }
             break;
 
         case 'u':
+            if (! logsetup)
+                setuplog("default log_info /dev/tty");
             parse_ldapconf();
             printf("Searching user: %s\n", optarg);
             ret = getuuidfromname( optarg, UUID_USER, uuid);
             if (ret == 0) {
-                uuid_bin2string( uuid, &uuidstring);
-                printf("User: %s ==> UUID: %s\n", optarg, uuidstring);
-                free(uuidstring);
+                printf("User: %s ==> UUID: %s\n", optarg, uuid_bin2string(uuid));
             } else {
                 printf("User %s not found.\n", optarg);
             }
             break;
 
         case 'g':
+            if (! logsetup)
+                setuplog("default log_info /dev/tty");
             parse_ldapconf();
             printf("Searching group: %s\n", optarg);
             ret = getuuidfromname( optarg, UUID_GROUP, uuid);
             if (ret == 0) {
-                uuid_bin2string( uuid, &uuidstring);
-                printf("Group: %s ==> UUID: %s\n", optarg, uuidstring);
-                free(uuidstring);
+                printf("Group: %s ==> UUID: %s\n", optarg, uuid_bin2string(uuid));
             } else {
                 printf("Group %s not found.\n", optarg);
             }
             break;
 
         case 'i':
+            if (! logsetup)
+                setuplog("default log_info /dev/tty");
             parse_ldapconf();
             printf("Searching uuid: %s\n", optarg);
             uuid_string2bin(optarg, uuid);
             ret = getnamefromuuid( uuid, &name, &type);
             if (ret == 0) {
-                if (type == UUID_USER)
+                switch (type) {
+                case UUID_LOCAL:
+                    printf("local UUID: %s\n", optarg);
+                    break;
+                case UUID_USER:
                     printf("UUID: %s ==> User: %s\n", optarg, name);
-                else
+                    break;
+                case UUID_GROUP:
                     printf("UUID: %s ==> Group: %s\n", optarg, name);
+                    break;
+                }
                 free(name);
             } else {
                 printf("UUID: %s not found.\n", optarg);
@@ -138,4 +150,3 @@ int main( int argc, char **argv)
     return 0;
 }
 
-#endif  /* HAVE_NFSv4_ACLS */
index 4b653521c9148f7fe9e394642881c0f166b80aad..e6d1daeb4d5fa470bb677687d3fcfc2ec7ad726d 100644 (file)
@@ -386,7 +386,7 @@ static int init(char* path)
 {
        DIR* startdir;
 
-    if (NULL == (cdb = cnid_open (path, 0, cnid_type, 0, NULL, NULL)) ) {
+    if (NULL == (cdb = cnid_open (path, 0, cnid_type, 0, "localhost", "4700")) ) {
                 fprintf (stderr, "ERROR: cannot open CNID database in '%s'\n", path);
                 fprintf (stderr, "ERROR: check the logs for reasons, aborting\n");
                return -1;
index d4453a70fac615573447571e95d5a41bfdd6d639..945672cc3d7880723e2781fbd397c3adb40bfc92 100644 (file)
 # illegalseq          -> encode illegal sequence in filename asis,
 #                        ex "\217-", which is not a valid SHIFT-JIS char,
 #                        is encoded  as U\217 -
-# acls                -> Enable ACLs on this volume. Requires a NFSv4 ACLs
-#                        compatible filesystem (e.g. ZFS) and an ACL API
-#                        compatible to *Solaris. In other words: this requires
-#                        Solaris, Opensolaris or a derived distribution.
 # nocnidcache         -> Don't store and read CNID to/from AppleDouble file.
 #                        This should not be used as it also prevents a CNID
 #                        database rebuild with `dbd`!
index ab07e1134ffb7822389652be30c4cf3d36f2cabe..8fbfddebf416965b02241f71329d9c92e8fe1950 100644 (file)
@@ -3,16 +3,33 @@
 SUBDIRS = pam
 SUFFIXES = .tmpl .
 
-GENFILES = afpd.conf AppleVolumes.default
 TMPLFILES = afpd.conf.tmpl AppleVolumes.default.tmpl
-CONFFILES = AppleVolumes.system atalkd.conf netatalk.conf papd.conf
-if USE_NFSv4_ACLS
+GENFILES = afpd.conf AppleVolumes.default
+CLEANFILES = $(GENFILES)
+EXTRA_DIST = \
+       AppleVolumes.default.tmpl \
+       AppleVolumes.system \
+       afp_ldap.conf \
+       afpd.conf.tmpl \
+       atalkd.conf \
+       netatalk.conf \
+       papd.conf
+
+OVERWRITE_CONFIG = @OVERWRITE_CONFIG@
+
+if USE_DEBIAN
+CONFFILES = AppleVolumes.system
+else
+CONFFILES = AppleVolumes.system netatalk.conf
+endif
+
+if HAVE_ACLS
 CONFFILES += afp_ldap.conf
 endif
 
-OVERWRITE_CONFIG = @OVERWRITE_CONFIG@
-EXTRA_DIST = $(CONFFILES) $(TMPLFILES) afp_ldap.conf
-CLEANFILES = $(GENFILES)
+if USE_APPLETALK
+CONFFILES += atalkd.conf papd.conf
+endif
 
 pkgconfdir = @PKGCONFDIR@
 webminpath = @WEBMIN_PATH@
@@ -43,7 +60,9 @@ uninstall-local:
                echo rm -f $(DESTDIR)$(pkgconfdir)/$$f; \
                rm -f $(DESTDIR)$(pkgconfdir)/$$f; \
        done
-
+if USE_DEBIAN
+       rm -f $(DESTDIR)/etc/default/netatalk
+endif
 
 install-config-files: $(CONFFILES) $(GENFILES)
        $(mkinstalldirs) $(DESTDIR)$(pkgconfdir)
@@ -55,6 +74,15 @@ install-config-files: $(CONFFILES) $(GENFILES)
                        echo "not overwriting $$f"; \
                fi; \
        done
+if USE_DEBIAN
+       $(mkinstalldirs) $(DESTDIR)/default
+       if test "x$(OVERWRITE_CONFIG)" = "xyes" -o ! -f /etc/default/netatalk; then \
+               echo "$(INSTALL_DATA) $(srcdir)/netatalk.conf $(DESTDIR)/etc/default/netatalk"; \
+               $(INSTALL_DATA) $(srcdir)/netatalk.conf $(DESTDIR)/etc/default/netatalk; \
+       else \
+               echo "not overwriting /etc/default/netatalk"; \
+       fi
+endif
 
 install-webmin:
        if test "x$(webminpath)" != "x"; then \
index 4c9ea98dbfa9243565069cdb968bfa0204c8d58f..0994a116d4821f1f2475fac5ccd889e1451e20db 100644 (file)
@@ -1,7 +1,8 @@
 #
 # CONFIGURATION FOR AFPD
 #
-# Each line defines a virtual server that should be available.
+# Each single line defines a virtual server that should be available.
+# Though, using "\" character, newline escaping is supported.
 # Empty lines and lines beginning with `#' are ignored.
 # Options in this file will override both compiled-in defaults
 # and command line options.
@@ -10,7 +11,7 @@
 
 #
 # Format:
-#  - [options]               to specify options for the default server
+#  - [options]               to specify options for the default server
 #  "Server name" [options]   to specify an additional server
 #
 
@@ -69,6 +70,8 @@
 #                         string.
 #     -slp                Register this server with the Service Location
 #                         Protocol (if SLP support was compiled in).
+#     -nozeroconf         Don't register this server with the Multicats
+#                         DNS Protocol.
 #     -advertise_ssh      Allows Mac OS X clients (10.3.3-10.4) to
 #                         automagically establish a tunneled AFP connection
 #                         through SSH. This option is not so significant
 #       4 servers w/ names server1-3 and one w/ the hostname. servers
 #       1-3 get routed to different ports with server 3 being bound 
 #       specifically to address 192.168.1.3
+#
 #           -
 #           server1 -port 12000
 #           server2 -port 12001
 #           server3 -port 12002 -ipaddr 192.168.1.3
 #
 #       a dedicated guest server, a user server, and a special
-#       ddp-only server:
-#           "Guest Volume" -uamlist uams_guest.so -loginmesg "Welcome guest!"
-#           "User Volume" -uamlist uams_clrtxt.so -port 12000
-#           "special" -notcp -defaultvol <path> -systemvol <path>
+#       AppleTalk-only server:
+#
+#           "Guest Server" -uamlist uams_guest.so \
+#                   -loginmesg "Welcome guest! I'm a public server."
+#           "User Server" -uamlist uams_dhx2.so -port 12000
+#           "special" -ddp -notcp -defaultvol <path> -systemvol <path>
 #
 
 
index ea893433b50e507e313c0eb402afb473659b24bc..b8c1e67b731325483351384e5f7a87c5c631efc9 100644 (file)
@@ -1,43 +1,60 @@
 # Netatalk configuration
-# Change this to increase the maximum number of clients that can connect:
-AFPD_MAX_CLIENTS=20
 
-# Change this to set the machine's atalk name and zone.
-# NOTE: if your zone has spaces in it, you're better off specifying
-#       it in afpd.conf
-#ATALK_ZONE=@zone
-ATALK_NAME=`echo ${HOSTNAME}|cut -d. -f1`
+#########################################################################
+# Global configuration
+#########################################################################
 
-# specify the Mac and unix charsets to be used
-ATALK_MAC_CHARSET='MAC_ROMAN'
+#### machine's AFPserver/AppleTalk name.
+#ATALK_NAME=machinename
+
+#### server (unix) and legacy client (<= Mac OS 9) charsets
 ATALK_UNIX_CHARSET='LOCALE'
+ATALK_MAC_CHARSET='MAC_ROMAN'
+
+#### Don't Edit. export the charsets, read form ENV by apps
+export ATALK_UNIX_CHARSET
+export ATALK_MAC_CHARSET
+
+#########################################################################
+# AFP specific configuration
+#########################################################################
 
-# specify the UAMs to enable
-# available options: uams_guest.so, uams_clrtxt.so, uams_randnum.so, 
-#                           uams_dhx.so, uams_dhx2.so
-# AFPD_UAMLIST="-U uams_dhx.so,uams_dhx2.so"
-
-# Change this to set the id of the guest user
-AFPD_GUEST=nobody
-
-# Set which daemons to run.
-# If you need legacy AppleTalk, run atalkd.
-# papd, timelord and a2boot are dependent upon atalkd.
-# If you use "AFP over TCP" server only, run only cnid_metad and afpd.
-ATALKD_RUN=no
-PAPD_RUN=no
-TIMELORD_RUN=no
-A2BOOT_RUN=no
+#### Set which daemons to run.
+#### If you use AFP file server, run both cnid_metad and afpd.
 CNID_METAD_RUN=yes
 AFPD_RUN=yes
 
-# Control whether the daemons are started in the background.
-# If it is dissatisfied that atalkd starts slowly, set "yes".
-ATALK_BGROUND=no
+#### maximum number of clients that can connect:
+#AFPD_MAX_CLIENTS=20
 
-# export the charsets, read form ENV by apps
-export ATALK_MAC_CHARSET
-export ATALK_UNIX_CHARSET
+#### UAMs (User Authentication Modules)
+#### available options: uams_dhx.so, uams_dhx2.so, uams_guest.so,
+####                    uams_clrtxt.so(legacy), uams_randnum.so(legacy)
+#AFPD_UAMLIST="-U uams_dhx.so,uams_dhx2.so"
+
+#### Set the id of the guest user when using uams_guest.so
+#AFPD_GUEST=nobody
+
+#### config for cnid_metad. Default log config:
+#CNID_CONFIG="-l log_note"
 
-# config for cnid_metad. Default log config:
-# CNID_CONFIG="-l log_note"
+#########################################################################
+# AppleTalk specific configuration (legacy)
+#########################################################################
+
+#### Set which legacy daemons to run.
+#### If you need AppleTalk, run atalkd.
+#### papd, timelord and a2boot are dependent upon atalkd.
+#ATALKD_RUN=no
+#PAPD_RUN=no
+#TIMELORD_RUN=no
+#A2BOOT_RUN=no
+
+#### Control whether the daemons are started in the background.
+#### If it is dissatisfied that legacy atalkd starts slowly, set "yes".
+#ATALK_BGROUND=no
+
+#### Set the AppleTalk Zone name.
+#### NOTE: if your zone has spaces in it, you're better off specifying
+####       it in afpd.conf
+#ATALK_ZONE=@zone
index 9b6e775a6e5408508f6607110148c042c947869d..48449921b926c766497b252d458c08cb237e17e0 100644 (file)
@@ -1,4 +1,3 @@
-dnl $Id: configure.in,v 1.244 2010-04-13 08:05:06 franklahm Exp $
 dnl configure.in for netatalk
 
 AC_INIT(etc/afpd/main.c)
@@ -25,71 +24,11 @@ AC_PROG_PS
 
 AM_PROG_CC_C_O
 
-dnl *********************************************************************
-dnl FIXME! FIXME! These should be selectable properly, and should produce
-dnl the proper flags and defines...
-dnl *********************************************************************
-
-############################################
-# we need dlopen/dlclose/dlsym/dlerror for PAM, the password database plugins and the plugin loading code
-#AC_SEARCH_LIBS(dlopen, [dl])
-# dlopen/dlclose/dlsym/dlerror will be checked again later and defines will be set then
-
-dnl Checks for libraries.
-dnl Replace `main' with a function in -labs:
-dnl AC_CHECK_LIB(abs, main)
-dnl Replace `main' with a function in -laudit:
-dnl AC_CHECK_LIB(audit, main)
-dnl Replace `main' with a function in -lauth:
-dnl AC_CHECK_LIB(auth, main)
-dnl Replace `main' with a function in -lcmd:
-dnl AC_CHECK_LIB(cmd, main)
-dnl Replace `main' with a function in -lcrypt:
-dnl AC_CHECK_LIB(crypt, main)
-dnl Replace `main' with a function in -ld:
-dnl AC_CHECK_LIB(d, main)
-dnl Replace `main' with a function in -ldl:
-dnl AC_CHECK_LIB(dl, dlopen)
-dnl Replace `main' with a function in -lkauth:
-dnl AC_CHECK_LIB(kauth, main)
-dnl Replace `main' with a function in -lkrb:
-dnl AC_CHECK_LIB(krb, main)
-dnl Replace `main' with a function in -llwp:
-dnl AC_CHECK_LIB(lwp, main)
-dnl Replace `main' with a function in -ln:
-dnl AC_CHECK_LIB(n, main)
-
-dnl not the right stuff but should be enough for now
-AC_CHECK_FUNC(gethostbyname,,[AC_CHECK_LIB(nsl,gethostbyname)])
-AC_CHECK_FUNC(connect,,[AC_CHECK_LIB(socket,connect)])
-
-dnl Replace `main' with a function in -lprot:
-dnl AC_CHECK_LIB(prot, main)
-dnl Replace `main' with a function in -lrx:
-dnl AC_CHECK_LIB(rx, main)
-dnl Replace `main' with a function in -lrxkad:
-dnl AC_CHECK_LIB(rxkad, main)
-dnl Replace `main' with a function in -lsys:
-dnl AC_CHECK_LIB(sys, main)
-dnl Replace `main' with a function in -lubik:
-dnl AC_CHECK_LIB(ubik, main)
-
-
-#
-# Check presence of some functions
-#
-# Check for XPG4 access() function
-# Be sure to test before adding AFS libs in LIBS path as AFS lib
-# has such a function that works only on AFS filesystems.
-AC_CHECK_FUNCS(access)
-# 
-AC_CHECK_FUNCS(pread pwrite)
-
 dnl Checks for header files.
 AC_HEADER_DIRENT
 AC_HEADER_STDC
 AC_HEADER_SYS_WAIT
-AC_CHECK_HEADERS(fcntl.h limits.h stdint.h strings.h time.h sys/param.h sys/fcntl.h sys/file.h sys/ioctl.h sys/time.h sys/mnttab.h sys/statvfs.h sys/stat.h sys/vfs.h mntent.h syslog.h unistd.h termios.h sys/termios.h netdb.h sgtty.h ufs/quota.h mount.h statfs.h sys/types.h dlfcn.h errno.h sys/errno.h sys/uio.h)
+AC_CHECK_HEADERS(fcntl.h limits.h stdint.h strings.h time.h sys/param.h sys/fcntl.h sys/file.h sys/ioctl.h sys/time.h sys/mnttab.h sys/statvfs.h sys/stat.h sys/vfs.h mntent.h syslog.h unistd.h termios.h sys/termios.h netdb.h sgtty.h ufs/quota.h mount.h statfs.h sys/types.h dlfcn.h errno.h sys/errno.h sys/uio.h langinfo.h locale.h sys/filio.h)
 AC_CHECK_HEADER(sys/cdefs.h,,
        AC_MSG_RESULT([enabling generic cdefs.h from tree])
        CFLAGS="-I\$(top_srcdir)/sys/generic $CFLAGS"
@@ -100,8 +39,6 @@ AC_CHECK_HEADERS([sys/mount.h], , ,
 #endif
 ])
 
-AC_CHECK_HEADERS(langinfo.h locale.h sys/filio.h)
-
 dnl Checks for typedefs, structures, and compiler characteristics.
 AC_C_CONST
 AC_TYPE_UID_T
@@ -124,7 +61,6 @@ if test x"$libltdl_cv_need_uscore" = xyes; then
     AC_DEFINE(DLSYM_PREPEND_UNDERSCORE, 1, [BSD compatibility macro])
 fi
 
-
 dnl Checks for library functions.
 AC_TYPE_GETGROUPS
 AC_PROG_GCC_TRADITIONAL
@@ -135,10 +71,26 @@ AC_TYPE_SIGNAL
 AC_FUNC_UTIME_NULL
 AC_FUNC_WAIT3
 AC_CHECK_FUNCS(getcwd gethostname gettimeofday getusershell mkdir rmdir select socket strdup strcasestr strstr strtoul strchr memcpy)
-AC_CHECK_FUNCS(backtrace_symbols setlocale nl_langinfo strlcpy strlcat setlinebuf dirfd pselect)
+AC_CHECK_FUNCS(backtrace_symbols setlocale nl_langinfo strlcpy strlcat setlinebuf dirfd pselect access pread pwrite)
 AC_CHECK_FUNCS(waitpid getcwd strdup strndup strnlen strtoul strerror chown fchown chmod fchmod chroot link mknod mknod64)
-AC_CHECK_FUNC(renameat, AC_DEFINE([_ATFILE_SOURCE], 1, AT file source)) 
+ac_neta_haveatfuncs=yes
+AC_CHECK_FUNCS(openat renameat fstatat unlinkat, , ac_neta_haveatfuncs=no)
+if test x"$ac_neta_haveatfuncs" = x"yes" ; then
+   AC_DEFINE([_ATFILE_SOURCE], 1, AT file source)
+   AC_DEFINE([HAVE_ATFUNCS], 1, whether at funcs are available)
+fi
+
 AC_CHECK_MEMBERS(struct tm.tm_gmtoff,,, [#include <time.h>])
+
+AC_CHECK_FUNC(gethostbyname,,[AC_CHECK_LIB(nsl,gethostbyname)])
+AC_CHECK_FUNC(connect,,[AC_CHECK_LIB(socket,connect)])
+dnl search for necessary libs for libpthread stuff
+AC_SEARCH_LIBS(pthread_sigmask, pthread,, 
+               [AC_MSG_ERROR([cannot find pthread_sigmask in libc or libpthread])])
+if test x"$ac_cv_search_pthread_sigmask" != x"none required" ; then
+   PTHREAD_LIBS=$ac_cv_search_pthread_sigmask
+fi
+AC_SUBST(PTHREAD_LIBS)
 AC_CACHE_SAVE
 
 dnl Checks for (v)snprintf
@@ -149,36 +101,34 @@ dnl 64bit platform check
 dnl --------------------------------------------------------------------------
 
 AC_MSG_CHECKING([whether to check for 64bit libraries])
-dnl Determine libdir name
-case $host in
-*-*-linux*)
-  # Test if the compiler is 64bit
-  echo 'int i;' > conftest.$ac_ext
-  atalk_cv_cc_64bit_output=no
-  if AC_TRY_EVAL(ac_compile); then
+# Test if the compiler is in 64bit mode
+echo 'int i;' > conftest.$ac_ext
+atalk_cv_cc_64bit_output=no
+if AC_TRY_EVAL(ac_compile); then
     case `/usr/bin/file conftest.$ac_objext` in
     *"ELF 64"*)
       atalk_cv_cc_64bit_output=yes
       ;;
     esac
-  fi
-  rm -rf conftest*
-  ;;
-esac
-
-dnl
-dnl FIXME: Do we need something like this for Solaris 64bit?
-dnl
+fi
+rm -rf conftest*
 
 case $host_cpu:$atalk_cv_cc_64bit_output in
-powerpc64:yes | s390x:yes | sparc64:yes | x86_64:yes)
-  atalk_libname="lib64"
-  AC_MSG_RESULT([yes])
-  ;;
+powerpc64:yes | s390x:yes | sparc*:yes | x86_64:yes | i386:yes)
+    AC_MSG_RESULT([yes])
+    case $target_os in
+    solaris2*)
+        atalk_libname="lib/64"
+        ;;
+    *)
+        atalk_libname="lib64"
+        ;;
+    esac
+    ;;
 *:*)
-  atalk_libname="lib"
-  AC_MSG_RESULT([no])
-  ;;
+    AC_MSG_RESULT([no])
+    atalk_libname="lib"
+    ;;
 esac
 
 dnl --------------------------------------------------------------------------
@@ -232,19 +182,20 @@ AC_ARG_WITH(cracklib,
 AC_MSG_CHECKING([for cracklib support])
 AC_MSG_RESULT([$netatalk_cv_with_cracklib])
 
-netatalk_cv_ddp_enabled=yes
+netatalk_cv_ddp_enabled=no
 AC_MSG_CHECKING([whether to enable DDP])
 AC_ARG_ENABLE(ddp,
-       [  --disable-ddp           disable DDP],[
-       if test "$enableval" = "no"; then 
-               AC_DEFINE(NO_DDP, 1, [Define if DDP should be disabled])
-               AC_MSG_RESULT([no])
-               netatalk_cv_ddp_enabled=no
+       [  --enable-ddp            enable DDP (AppleTalk)],[
+       if test "$enableval" = "yes"; then 
+               AC_MSG_RESULT([yes])
+               netatalk_cv_ddp_enabled=yes
        else
                AC_MSG_RESULT([yes])
+               AC_DEFINE(NO_DDP, 1, [Define if DDP should be disabled])
        fi
        ],[
-               AC_MSG_RESULT([yes])
+               AC_MSG_RESULT([no])
+               AC_DEFINE(NO_DDP, 1, [Define if DDP should be disabled])
        ]
 )
 
@@ -278,9 +229,11 @@ AC_ARG_ENABLE(debug,
                AC_MSG_RESULT([yes])
        else
                AC_MSG_RESULT([no])
+        AC_DEFINE(NDEBUG, 1, [Disable assertions])
        fi
        ],[
                AC_MSG_RESULT([no])
+        AC_DEFINE(NDEBUG, 1, [Disable assertions])
        ]
 )
 
@@ -302,39 +255,7 @@ AC_ARG_ENABLE(debugging,
        ]
 )
 
-
-afp3=no
-afp3set=no
-AC_MSG_CHECKING([whether AFP 3.x calls should be enabled])
-AC_ARG_ENABLE(afp3,
-       [  --disable-afp3          disable AFP 3.x calls],
-       [
-           if test x"$enableval" != x"no"; then
-               afp3set=yes
-               afp3=yes
-               AC_MSG_RESULT([yes])
-           else
-               AC_MSG_RESULT([no])
-           fi
-       ],[
-           AC_MSG_RESULT([yes])
-           afp3=yes
-       ]
-)
-
-if test x"$afp3" = x"yes"; then
-        AC_SYS_LARGEFILE([
-               AC_DEFINE(AFP3x, 1, [Define to enable AFP 3.x support])
-       ],[
-               if test x"$afp3set" = x"yes"; then
-                       AC_MSG_ERROR([AFP 3.x support requires Large File Support.])
-               else
-                       AC_MSG_WARN([AFP 3.x support requires Large File Support. AFP3.x support disabled])
-                       afp3=no
-               fi
-       ])
-fi
-
+AC_SYS_LARGEFILE([], AC_MSG_ERROR([AFP 3.x support requires Large File Support.]))
 AC_CHECK_ICONV
 
 dnl ----------- A NOTE ABOUT DROPKLUDGE
@@ -387,6 +308,9 @@ AC_CHECK_QUOTA
 dnl Check for optional server location protocol support (used by MacOS X)
 NETATALK_SRVLOC
 
+dnl Check for optional Zeroconf support
+NETATALK_ZEROCONF
+
 dnl Check for PAM libs
 netatalk_cv_use_pam=no
 AC_PATH_PAM([
@@ -828,7 +752,7 @@ if test x"$this_os" = "xsolaris"; then
 
        solaris_module=no
        AC_MSG_CHECKING([if we can build Solaris kernel module])
-       if test -x /usr/ccs/bin/ld && test x$netatalk_cv_ddp_enabled = yes ; then
+       if test -x /usr/ccs/bin/ld && test x"$netatalk_cv_ddp_enabled" = x"yes" ; then
                solaris_module=yes
        fi
        AC_MSG_RESULT([$solaris_module])
@@ -1022,49 +946,153 @@ AC_ARG_ENABLE(overwrite,
 )
 AC_MSG_RESULT([$OVERWRITE_CONFIG])
 
-dnl --------------------- check for ACL support
-AC_MSG_CHECKING([if NFSv4 ACL Support should be enabled])
-AC_ARG_ENABLE(nfsv4acls,
-       [  --enable-nfsv4acls      enable NFSv4 ACL Support (auto)],[
-       if test x"$enableval" = x"yes"; then
-          AC_MSG_RESULT([yes])
-          neta_cv_nfsv4acl="yes"
-       else
-          AC_MSG_RESULT([no])
-          neta_cv_nfsv4acl="no"
-       fi],[
-          AC_MSG_RESULT([auto])
-          neta_cv_nfsv4acl="yes"
-       ]
-)
+dnl --------------------- check for LDAP support, for client-side ACL visibility
+AC_MSG_CHECKING(for LDAP (necessary for client-side ACL visibility))
+AC_ARG_WITH(ldap,
+    [AS_HELP_STRING([--with-ldap],
+        [LDAP support (default=auto)])],
+    [ case "$withval" in
+      yes|no)
+          with_ldap="$withval"
+                 ;;
+      *)
+          with_ldap=auto
+          ;;
+      esac ])
+AC_MSG_RESULT($with_ldap)
+
+if test x"$with_ldap" != x"no" ; then
+       AC_CHECK_HEADER([ldap.h], with_ldap=yes,
+        [ if test x"$with_ldap" = x"yes" ; then
+            AC_MSG_ERROR([Missing LDAP headers])
+        fi
+               with_ldap=no
+        ])
+       AC_CHECK_LIB(ldap, ldap_init, with_ldap=yes,
+        [ if test x"$with_ldap" = x"yes" ; then
+            AC_MSG_ERROR([Missing LDAP library])
+        fi
+               with_ldap=no
+        ])
+fi
 
-if test x"$this_os" != x"solaris" && test x"$neta_cv_nfsv4acl" = x"yes" ; then
-               AC_MSG_NOTICE([NFSv4 ACL Support only available on (Open)Solaris])
-        neta_cv_nfsv4acl="no"
+if test x"$with_ldap" = x"yes"; then
+       AC_DEFINE(HAVE_LDAP,1,[Whether LDAP is available])
 fi
 
-if test x$neta_cv_nfsv4acl = xyes; then
-       AC_CHECK_HEADER([ldap.h],,[
-               AC_MSG_ERROR([ACL Support need the LDAP client headers not found.])
-               neta_cv_nfsv4acl=no
-               ]
-       )
-       AC_CHECK_LIB(ldap,ldap_init,neta_cv_nfsv4acl=yes,neta_cv_nfsv4acl=no)
+dnl --------------------- check for ACL support
+AC_MSG_CHECKING(whether to support ACLs)
+AC_ARG_WITH(acls,
+    [AS_HELP_STRING([--with-acls],
+        [Include ACL support (default=auto)])],
+    [ case "$withval" in
+      yes|no)
+          with_acl_support="$withval"
+                 ;;
+      *)
+          with_acl_support=auto
+          ;;
+      esac ],
+    [with_acl_support=auto])
+AC_MSG_RESULT($with_acl_support)
+
+if test x"$with_acl_support" = x"no"; then
+       AC_MSG_RESULT(Disabling ACL support)
+       AC_DEFINE(HAVE_NO_ACLS,1,[Whether no ACLs support should be built in])
+else
+    with_acl_support=yes
 fi
-if test x$neta_cv_nfsv4acl = xyes; then
-       AC_CHECK_HEADER([sys/acl.h],[
-               AC_DEFINE([HAVE_NFSv4_ACLS], 1, [Enable ACL code])
-               AC_MSG_NOTICE([Enabling ACL support])
-               ],
-               neta_cv_nfsv4acl=no
-       )
+
+if test x"$with_acl_support" = x"yes" ; then
+       AC_MSG_NOTICE(checking whether ACL support is available:)
+       case "$host_os" in
+       *sysv5*)
+               AC_MSG_NOTICE(Using UnixWare ACLs)
+               AC_DEFINE(HAVE_UNIXWARE_ACLS,1,[Whether UnixWare ACLs are available])
+               ;;
+       *solaris*)
+               AC_MSG_NOTICE(Using solaris ACLs)
+               AC_DEFINE(HAVE_SOLARIS_ACLS,1,[Whether solaris ACLs are available])
+               ACL_LIBS="$ACL_LIBS -lsec"
+               ;;
+       *hpux*)
+               AC_MSG_NOTICE(Using HPUX ACLs)
+               AC_DEFINE(HAVE_HPUX_ACLS,1,[Whether HPUX ACLs are available])
+               ;;
+       *irix*)
+               AC_MSG_NOTICE(Using IRIX ACLs)
+               AC_DEFINE(HAVE_IRIX_ACLS,1,[Whether IRIX ACLs are available])
+               ;;
+       *aix*)
+               AC_MSG_NOTICE(Using AIX ACLs)
+               AC_DEFINE(HAVE_AIX_ACLS,1,[Whether AIX ACLs are available])
+               ;;
+       *osf*)
+               AC_MSG_NOTICE(Using Tru64 ACLs)
+               AC_DEFINE(HAVE_TRU64_ACLS,1,[Whether Tru64 ACLs are available])
+               ACL_LIBS="$ACL_LIBS -lpacl"
+               ;;
+       *darwin*)
+               AC_MSG_NOTICE(ACLs on Darwin currently not supported)
+               AC_DEFINE(HAVE_NO_ACLS,1,[Whether no ACLs support is available])
+               ;;
+       *)
+               AC_CHECK_LIB(acl,acl_get_file,[ACL_LIBS="$ACL_LIBS -lacl"])
+               case "$host_os" in
+               *linux*)
+                       AC_CHECK_LIB(attr,getxattr,[ACL_LIBS="$ACL_LIBS -lattr"])
+                       ;;
+               esac
+               AC_CACHE_CHECK([for POSIX ACL support],netatalk_cv_HAVE_POSIX_ACLS,[
+                       acl_LIBS=$LIBS
+                       LIBS="$LIBS $ACL_LIBS"
+                       AC_TRY_LINK([
+                               #include <sys/types.h>
+                               #include <sys/acl.h>
+                       ],[
+                               acl_t acl;
+                               int entry_id;
+                               acl_entry_t *entry_p;
+                               return acl_get_entry(acl, entry_id, entry_p);
+                       ],
+                       [netatalk_cv_HAVE_POSIX_ACLS=yes],
+                       [netatalk_cv_HAVE_POSIX_ACLS=no
+                with_acl_support=no])
+                       LIBS=$acl_LIBS
+               ])
+               if test x"$netatalk_cv_HAVE_POSIX_ACLS" = x"yes"; then
+                       AC_MSG_NOTICE(Using POSIX ACLs)
+                       AC_DEFINE(HAVE_POSIX_ACLS,1,[Whether POSIX ACLs are available])
+                       AC_CACHE_CHECK([for acl_get_perm_np],netatalk_cv_HAVE_ACL_GET_PERM_NP,[
+                               acl_LIBS=$LIBS
+                               LIBS="$LIBS $ACL_LIBS"
+                               AC_TRY_LINK([
+                                       #include <sys/types.h>
+                                       #include <sys/acl.h>
+                               ],[
+                                       acl_permset_t permset_d;
+                                       acl_perm_t perm;
+                                       return acl_get_perm_np(permset_d, perm);
+                               ],
+                               [samba_cv_HAVE_ACL_GET_PERM_NP=yes],
+                               [samba_cv_HAVE_ACL_GET_PERM_NP=no])
+                               LIBS=$acl_LIBS
+                       ])
+                       if test x"netatalk_cv_HAVE_ACL_GET_PERM_NP" = x"yes"; then
+                               AC_DEFINE(HAVE_ACL_GET_PERM_NP,1,[Whether acl_get_perm_np() is available])
+                       fi
+               else
+                       AC_MSG_NOTICE(ACL support is not avaliable)
+                       AC_DEFINE(HAVE_NO_ACLS,1,[Whether no ACLs support is available])
+               fi
+               ;;
+    esac
 fi
-if test x$neta_cv_nfsv4acl = xyes; then
-       LIBATALK_ACLS="acl/libacl.la"
-else
-       LIBATALK_ACLS=""
+
+if test x"$with_acl_support" = x"yes" ; then
+    AC_DEFINE(HAVE_ACLS,1,[Whether ACLs support is available])
+    AC_SUBST(ACL_LIBS)
 fi
-AC_SUBST(LIBATALK_ACLS)
 
 dnl --------------------- check for Extended Attributes support
 neta_cv_eas="ad"
@@ -1166,6 +1194,7 @@ if test "x$neta_cv_eas_sys_found" = "xyes" ; then
       neta_cv_eas="$neta_cv_eas | sys"
    fi
 fi
+AC_DEFINE_UNQUOTED(EA_MODULES,["$neta_cv_eas"],[Available Extended Attributes modules])
 
 dnl --------------------- Check if realpath() takes NULL
 AC_CACHE_CHECK([if the realpath function allows a NULL argument],
@@ -1210,7 +1239,8 @@ AM_CONDITIONAL(COMPILE_TIMELORD, test x$compile_timelord = xyes)
 AM_CONDITIONAL(COMPILE_A2BOOT, test x$compile_a2boot = xyes)
 AM_CONDITIONAL(HAVE_LIBGCRYPT, test x$neta_cv_have_libgcrypt = xyes)
 AM_CONDITIONAL(HAVE_OPENSSL, test x$neta_cv_have_openssl = xyes)
-AM_CONDITIONAL(USE_NFSv4_ACLS, test x$neta_cv_nfsv4acl = xyes)
+AM_CONDITIONAL(HAVE_ACLS, test x"$with_acl_support" = x"yes")
+AM_CONDITIONAL(HAVE_LDAP, test x"$with_ldap" = x"yes")
 AM_CONDITIONAL(USE_DHX, test x$neta_cv_compile_dhx = xyes)
 AM_CONDITIONAL(USE_DHX2, test x$neta_cv_compile_dhx2 = xyes)
 AM_CONDITIONAL(USE_RANDNUM, test x$neta_cv_have_openssl = xyes)
@@ -1231,14 +1261,16 @@ AM_CONDITIONAL(USE_GENTOO, test x$sysv_style = xgentoo)
 AM_CONDITIONAL(USE_DEBIAN, test x$sysv_style = xdebian)
 AM_CONDITIONAL(USE_UNDEF, test x$sysv_style = x)
 AM_CONDITIONAL(USE_BDB, test x$bdb_required = xyes)
+AM_CONDITIONAL(USE_APPLETALK, test x$netatalk_cv_ddp_enabled = xyes)
+AM_CONDITIONAL(HAVE_ATFUNCS, test x"$ac_neta_haveatfuncs" = x"yes")
 
 dnl --------------------- generate files
 
 AC_OUTPUT([Makefile
        bin/Makefile
+       bin/ad/Makefile
        bin/adv1tov2/Makefile
        bin/aecho/Makefile
-       bin/afile/Makefile
        bin/afppasswd/Makefile
        bin/cnid/Makefile
        bin/cnid/cnid2_create
@@ -1257,10 +1289,7 @@ AC_OUTPUT([Makefile
        contrib/printing/Makefile
        contrib/shell_utils/Makefile
        contrib/shell_utils/afpd-mtab.pl
-       contrib/shell_utils/apple_cp
     contrib/shell_utils/apple_dump
-       contrib/shell_utils/apple_mv
-       contrib/shell_utils/apple_rm
        contrib/shell_utils/asip-status.pl
        contrib/timelord/Makefile
        contrib/a2boot/Makefile
@@ -1285,6 +1314,7 @@ AC_OUTPUT([Makefile
        libatalk/adouble/Makefile
        libatalk/asp/Makefile
        libatalk/atp/Makefile
+       libatalk/bstring/Makefile
        libatalk/cnid/Makefile
        libatalk/cnid/cdb/Makefile
        libatalk/cnid/last/Makefile
@@ -1295,7 +1325,6 @@ AC_OUTPUT([Makefile
        libatalk/nbp/Makefile
        libatalk/netddp/Makefile
        libatalk/util/Makefile
-       libatalk/util/test/Makefile
        libatalk/tdb/Makefile
        libatalk/unicode/Makefile
        libatalk/unicode/charsets/Makefile
@@ -1316,6 +1345,8 @@ AC_OUTPUT([Makefile
        sys/solaris/Makefile
        sys/sunos/Makefile
        sys/ultrix/Makefile
+       test/Makefile
+       test/afpd/Makefile
        ],
        [chmod a+x distrib/config/netatalk-config contrib/shell_utils/apple_*]
 )
diff --git a/contrib/ICDumpSuffixMap b/contrib/ICDumpSuffixMap
deleted file mode 100644 (file)
index 3a8283f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#!perl\r#\r# ICDumpMap\r#     ---  Dump suffix mappings from your Internet Config extension.\r#\r# iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>\r#\r\ruse Mac::InternetConfig;\r\ropen MAP, ">AppleVolumes";\rprintf MAP "%-9s \"%4s\"  \"%4s\"      %-30s %-25s %-15s\n\n",\r".", "TEXT", "ttxt", "ASCII Text", "SimpleText", "text/plain";\rprint MAP "\# The following lines are extracted from Internet Config Preference.\n\n";\rfor my $entry (keys %InternetConfigMap) {\r   next unless $entry->extension =~ /^\./;\r   $_ = sprintf "%-9s \"%4s\"  \"%4s\"      %-30s %-25s %-15s",\r      $entry->extension, $entry->file_type, $entry->file_creator,\r      $entry->entry_name, $entry->creator_app_name,\r      $entry->MIME_type;\r   s/\s*$/\n/;\r   print MAP;\r}\rclose MAP;\r
\ No newline at end of file
index ae0ea4f003f6226870b7cda5e8766b5fbaaad15e..5652b1bb74d2ad283927a2c398cc40cba3b43da9 100644 (file)
@@ -1,17 +1,15 @@
 # Makefile.am for contrib/
 
+SUBDIRS = macusers shell_utils
+
 if COMPILE_TIMELORD
-TIMELORD = timelord
-else
-TIMELORD =
+SUBDIRS += timelord
 endif
 
 if COMPILE_A2BOOT
-A2BOOT = a2boot
-else
-A2BOOT =
+SUBDIRS += a2boot
 endif
 
-SUBDIRS = macusers printing shell_utils ${TIMELORD} ${A2BOOT}
-
-EXTRA_DIST = ICDumpSuffixMap
+if USE_APPLETALK
+SUBDIRS += printing
+endif
index 64aa0f59dcff2daa2132c937f19d19d357b6475a..75ddb5c4366e41eb78b2e0d432a9209ae5a06470 100644 (file)
@@ -5,21 +5,17 @@ use Socket;
 use vars qw($MAC_PROCESS $PS_STR $MATCH_STR $ASIP_PORT_NO $ASIP_PORT $LSOF);
 
 # Written for linux; may have to be modified for your brand of Unix.
-
 # Support for FreeBSD added by Joe Clarke <marcus@marcuscom.com>.
-# Support could probably be extended for *BSD, but I do not have Net or
-# OpenBSD machines to test with.  Code has also been cleaned up and made
-# to compile under strict.
-#
-# The new lsof call should also be quicker as it does not involve a 
-# second pipeline.
-#
+# Support Solaris added by Frank Lahm <franklahm@googlemail.com>.
 # Support has also been added for 16 character usernames.
 
 $MAC_PROCESS = "afpd";
 if ($^O eq "freebsd" || $^O eq "openbsd") {
         $PS_STR    = "-awwxouser,pid,ppid,start,command";
         $MATCH_STR = '(\w+)\s+(\d+)\s+(\d+)\s+([\d\w:]+)';
+} elsif ($^O eq "solaris") {
+        $PS_STR    = "-eo user,pid,ppid,c,stime,tty,time,comm";
+        $MATCH_STR = '\s*(\w+)\s+(\d+)\s+(\d+)\s+\d+\s+([\d\w:]+)';
 } else {
         $PS_STR    = "-eo user:32,pid,ppid,c,stime,tty,time,cmd";
         $MATCH_STR = '\s*(\w+)\s+(\d+)\s+(\d+)\s+\d+\s+([\d\w:]+)';
@@ -48,6 +44,12 @@ if ($^O eq "freebsd") {
         print
             "PID      UID      Username         Name                 Logintime Mac\n";
         close(SOCKSTAT);
+} elsif ($^O eq "solaris") {
+        if ($< != 0) {
+            print "must be run as root\n";
+            exit(1);
+        }
+        print "PID      UID      Username         Name                 Logintime Mac\n";
 } elsif ($LSOF == 1) {
         open(LSOF, "lsof -i :$ASIP_PORT |");
 
@@ -72,7 +74,7 @@ open(PS, "ps $PS_STR |") || die "Unable to open a pipe to ``ps''";
 
 while (<PS>) {
         next if ($_ !~ /$MAC_PROCESS/);
-        my ($user, $pid, $ppid, $time, $name, $uid, $t);
+        my ($user, $pid, $ppid, $time, $name, $uid, $t, $ip);
         $_ =~ /$MATCH_STR/;
         $user = $1;
         $pid  = $2;
@@ -80,6 +82,26 @@ while (<PS>) {
         $time = $4;
 
         if ($ppid != 1) {
+                if ($^O eq "solaris") {
+                        open(PFILES, "pfiles $pid |");
+                        while (<PFILES>) {
+                                next if ($_ !~ /port: $ASIP_PORT_NO/);
+                                while (<PFILES>) {
+                                        next if ($_ !~ /peername/);
+                                        if ($_ =~ /AF_INET (.*) port/) {
+                                            $ip = $1;
+                                            if ($ip =~ /::ffff:(.*)/ ) {
+                                                $ip = $1;
+                                            }
+                                        }
+                                        $mac{$pid} = $ip;
+                                        last;
+                                }
+                                last;
+                        }
+                        close(PFILES);
+                }
+
                 ($t, $t, $uid, $t, $t, $t, $name, $t, $t) = getpwnam($user);
                 ($name) = ( $name =~ /(^[^,]+)/ );
                 printf "%-8d %-8d %-16s %-20s %-9s %s\n", $pid, $uid, $user,
diff --git a/contrib/misc/make-casetable.pl b/contrib/misc/make-casetable.pl
new file mode 100755 (executable)
index 0000000..68bcfc9
--- /dev/null
@@ -0,0 +1,324 @@
+#!/usr/bin/perl
+#
+# usage: make-casetable.pl <infile> <outfile1> <outfile2>
+#        make-casetable.pl UnicodeData.txt utf16_casetable.h utf16_case.c
+#
+# (c) 2011 by HAT <hat@fa2.so-net.ne.jp>
+#
+#  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.
+#
+
+# See
+# http://www.unicode.org/reports/tr44/
+# http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
+
+# One block has 64 chars.
+#
+# BMP
+# block    0 = dummy
+# block    1 = U+0000 - U+003F
+# block    2 = U+0040 - U+007F
+# .....
+# block 1024 = U+FFC0 - U+FFFF
+# block 1025 = dummy
+#
+# Surrogate Pair
+# block  1024 = dummy
+# block  1025 = U+010000 - U+01003F
+# block  1026 = U+010040 - U+01007F
+# .....
+# block 17408 = U+10FFC0 - U+10FFFF
+# block 17409 = dummy
+#
+# Dummy block is for edge detection.
+# If block include upper/lower chars, block_enable[]=1.
+
+use strict;
+use warnings;
+
+our $code0;
+our $Name1;
+our $General_Category2;
+our $Canonical_Combining_Class3;
+our $Bidi_Class4;
+our $Decomposition_Mapping5;
+our $Numeric_Value6;
+our $Numeric_Value7;
+our $Numeric_Value8;
+our $Bidi_Mirrored9;
+our $Unicode_1_Name10;
+our $ISO_Comment11;
+our $Simple_Uppercase_Mapping12;
+our $Simple_Lowercase_Mapping13;
+our $Simple_Titlecase_Mapping14;
+
+our $hex_code0;
+our $Mapping;
+our $hex_Mapping;
+
+our $char;
+our $sp;
+our $block;
+
+our @table;
+our @table_sp;
+
+our @block_enable;
+our @block_enable_sp;
+
+our $table_no;
+our $block_start;
+our $block_end;
+our $char_start;
+our $char_end;
+
+open(CHEADER, ">$ARGV[1]");
+open(CSOURCE, ">$ARGV[2]");
+
+printf (CHEADER "\/\*\n");
+printf (CHEADER "DO NOT EDIT BY HAND\!\!\!\n");
+printf (CHEADER "\n");
+printf (CHEADER "This file is generated by\n");
+printf (CHEADER " contrib/misc/make-casetable.pl %s %s %s\n", $ARGV[0], $ARGV[1], $ARGV[2]);
+printf (CHEADER "\n");
+printf (CHEADER "%s is got from\n", $ARGV[0]);
+printf (CHEADER "http\:\/\/www.unicode.org\/Public\/UNIDATA\/UnicodeData.txt\n");
+printf (CHEADER "\*\/\n");
+printf (CHEADER "\n");
+
+printf (CSOURCE "\/\*\n");
+printf (CSOURCE "DO NOT EDIT BY HAND\!\!\!\n");
+printf (CSOURCE "\n");
+printf (CSOURCE "This file is generated by\n");
+printf (CSOURCE " contrib/misc/make-casetable.pl %s %s %s\n", $ARGV[0], $ARGV[1], $ARGV[2]);
+printf (CSOURCE "\n");
+printf (CSOURCE "%s is got from\n", $ARGV[0]);
+printf (CSOURCE "http\:\/\/www.unicode.org\/Public\/UNIDATA\/UnicodeData.txt\n");
+printf (CSOURCE "\*\/\n");
+printf (CSOURCE "\n");
+printf (CSOURCE "\#include \<netatalk\/endian.h\>\n");
+printf (CSOURCE "\#include \<atalk\/unicode.h\>\n");
+printf (CSOURCE "\#include \"%s\"\n", $ARGV[1]);
+printf (CSOURCE "\n");
+
+&make_array("upper");
+&make_array("lower");
+
+printf (CHEADER "\/\* EOF \*\/\n");
+printf (CSOURCE "\/\* EOF \*\/\n");
+
+close(CHEADER);
+close(CSOURCE);
+
+
+###########################################################################
+sub make_array{
+
+    # init table -----------------------------------------------------
+
+    for ($char = 0 ; $char <= 0xFFFF ; $char++) {
+        $table[$char][0] = $char;       # mapped char
+        $table[$char][1] = $char;       # orig char
+        $table[$char][2] = "";          # char name
+    }
+
+    for ($char = 0x10000 ; $char <= 0x10FFFF ; $char++) {
+        $sp = ((0xD800 - (0x10000 >> 10) + ($char >> 10)) << 16)
+            + (0xDC00 + ($char & 0x3FF));
+        $table_sp[$char][0] = $sp;      # mapped surrogate pair
+        $table_sp[$char][1] = $sp;      # orig surrogate pair
+        $table_sp[$char][2] = $char;    # mapped char
+        $table_sp[$char][3] = $char;    # orig char
+        $table_sp[$char][4] = "";       # char name
+    }
+
+    for ($block = 0 ; $block <= 1025 ; $block++) {
+        $block_enable[$block] = 0;
+    }
+
+    $block_enable[1] = 1;           # ASCII block is forcibly included
+    $block_enable[2] = 1;           # in the array for Speed-Up.
+
+    for ($block = 1024 ; $block <= 17409 ; $block++) {
+        $block_enable_sp[$block] = 0;
+    }
+
+    # write data to table --------------------------------------------
+
+    open(UNICODEDATA, "<$ARGV[0]");
+
+    while (<UNICODEDATA>) {
+        chop;
+        (
+            $code0,
+            $Name1,
+            $General_Category2,
+            $Canonical_Combining_Class3,
+            $Bidi_Class4,
+            $Decomposition_Mapping5,
+            $Numeric_Value6,
+            $Numeric_Value7,
+            $Numeric_Value8,
+            $Bidi_Mirrored9,
+            $Unicode_1_Name10,
+            $ISO_Comment11,
+            $Simple_Uppercase_Mapping12,
+            $Simple_Lowercase_Mapping13,
+            $Simple_Titlecase_Mapping14
+        ) = split(/\;/);
+
+        if ($_[0] eq "upper") {
+            $Mapping = $Simple_Uppercase_Mapping12;
+        } elsif ($_[0] eq "lower") {
+            $Mapping = $Simple_Lowercase_Mapping13;
+        } else {
+            exit(1);
+        }
+
+        next if ($Mapping eq "");
+
+        $hex_code0 = hex($code0);
+        $hex_Mapping = hex($Mapping);
+
+        if ($hex_code0 <= 0xFFFF) {
+            $table[$hex_code0][0] = $hex_Mapping;
+            #table[$hex_code0][1]   already set
+            $table[$hex_code0][2] = $Name1;
+            $block_enable[($hex_code0 / 64) +1] = 1;
+        } else {
+            $sp = ((0xD800 - (0x10000 >> 10) + ($hex_Mapping >> 10)) << 16)
+                + (0xDC00 + ($hex_Mapping & 0x3FF));
+            $table_sp[$hex_code0][0] = $sp;
+            #table_sp[$hex_code0][1]   already set
+            $table_sp[$hex_code0][2] = $hex_Mapping;
+            #table_sp[$hex_code0][3]   already set
+            $table_sp[$hex_code0][4] = $Name1;
+            $block_enable_sp[($hex_code0 / 64) +1] = 1;
+        }
+    }
+
+    close(UNICODEDATA);
+
+    # array for BMP --------------------------------------------------
+
+    printf(CSOURCE "\/*******************************************************************\n");
+    printf(CSOURCE " Convert a wide character to %s case.\n", $_[0]);
+    printf(CSOURCE "*******************************************************************\/\n");
+    printf(CSOURCE "ucs2\_t to%s\_w\(ucs2\_t val\)\n", $_[0]);
+    printf(CSOURCE "{\n");
+
+    $table_no = 1;
+
+    for ($block = 1 ; $block <= 1024 ; $block++) {
+
+        # rising edge detection
+        if ($block_enable[$block - 1] == 0 && $block_enable[$block] == 1) {
+            $block_start = $block;
+        }
+
+        # falling edge detection
+        if ($block_enable[$block] == 1 && $block_enable[$block + 1] == 0) {
+            $block_end = $block;
+
+            $char_start = ($block_start -1)* 64;
+            $char_end = ($block_end * 64) -1;
+
+            printf(CHEADER "static const u\_int16\_t %s\_table\_%d\[%d\] \= \{\n",
+                   $_[0], $table_no, $char_end - $char_start +1);
+
+            for ($char = $char_start ; $char <= $char_end ; $char++) {
+                printf(CHEADER "  0x%04X, /*U\+%04X*/ /*%s*/\n",
+                       $table[$char][0],
+                       $table[$char][1],
+                       $table[$char][2]
+                   );
+            }
+            printf(CHEADER "\}\;\n");
+            printf(CHEADER "\n");
+
+            if ($char_start == 0x0000) {
+                printf(CSOURCE "    if \( val \<\= 0x%04X)\n",
+                       $char_end);
+                printf(CSOURCE "        return %s\_table\_%d\[val]\;\n",
+                       $_[0], $table_no);
+            } else {
+                printf(CSOURCE "    if \( val \>\= 0x%04X \&\& val \<\= 0x%04X)\n",
+                       $char_start, $char_end);
+                printf(CSOURCE "        return %s\_table\_%d\[val-0x%04X\]\;\n",
+                       $_[0], $table_no, $char_start);
+            }
+            printf(CSOURCE "\n");
+
+            $table_no++;
+        }
+    }
+
+    printf(CSOURCE "\treturn \(val\)\;\n");
+    printf(CSOURCE "\}\n");
+    printf(CSOURCE "\n");
+
+    # array for Surrogate Pair ---------------------------------------
+
+    printf(CSOURCE "\/*******************************************************************\n");
+    printf(CSOURCE " Convert a surrogate pair to %s case.\n", $_[0]);
+    printf(CSOURCE "*******************************************************************\/\n");
+    printf(CSOURCE "u\_int32\_t to%s\_sp\(u\_int32\_t val\)\n", $_[0]);
+    printf(CSOURCE "{\n");
+
+    $table_no = 1;
+
+    for ($block = 1025 ; $block <= 17408 ; $block++) {
+
+        # rising edge detection
+        if ((($block_enable_sp[$block - 1] == 0) || ((($block - 1) & 0xF) == 0))
+                && ($block_enable_sp[$block] == 1)) {
+            $block_start = $block;
+        }
+
+        # falling edge detection
+        if (($block_enable_sp[$block] == 1) &&
+                ((($block - 1) & 0xF == 0xF) || ($block_enable_sp[$block + 1] == 0))) {
+            $block_end = $block;
+
+            $char_start = ($block_start -1)* 64;
+            $char_end = ($block_end * 64) -1;
+
+            printf(CHEADER "static const u\_int32\_t %s\_table\_sp\_%d\[%d\] \= \{\n",
+                   $_[0], $table_no, $char_end - $char_start +1);
+
+            for ($char = $char_start ; $char <= $char_end ; $char++) {
+                printf(CHEADER "  0x%08X, /*0x%08X*/ /*U\+%06X*/ /*U\+%06X*/ /*%s*/\n",
+                       $table_sp[$char][0],
+                       $table_sp[$char][1],
+                       $table_sp[$char][2],
+                       $table_sp[$char][3],
+                       $table_sp[$char][4]
+                   );
+            }
+            printf(CHEADER "\}\;\n");
+            printf(CHEADER "\n");
+
+            printf(CSOURCE "    if \( val \>\= 0x%08X \&\& val \<\= 0x%08X)\n",
+                   $table_sp[$char_start][1], $table_sp[$char_end][1]);
+            printf(CSOURCE "        return %s\_table\_sp\_%d\[val-0x%08X\]\;\n",
+                   $_[0], $table_no, $table_sp[$char_start][1]);
+            printf(CSOURCE "\n");
+
+            $table_no++;
+        }
+    }
+
+    printf(CSOURCE "\treturn \(val\)\;\n");
+    printf(CSOURCE "\}\n");
+    printf(CSOURCE "\n");
+}
+
+# EOF
old mode 100644 (file)
new mode 100755 (executable)
index 9e85815..11cb8d9
@@ -1,6 +1,19 @@
 #!/usr/bin/perl
-
+#
 # usage: make-precompose.h.pl UnicodeData.txt > precompose.h
+#
+# (c) 2008-2011 by HAT <hat@fa2.so-net.ne.jp>
+#
+#  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.
+# 
 
 # See
 # http://www.unicode.org/Public/UNIDATA/UCD.html
 # http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
 
 
-# table for binary search --------------------------------------------------
+# temp files for binary search (compose.TEMP, compose_sp.TEMP) -------------
 
 open(UNICODEDATA, "<$ARGV[0]");
-open(PRECOMPOSETEMP, ">precompose.TEMP");
-open( DECOMPOSETEMP, ">decompose.TEMP");
 
-while (<UNICODEDATA>){
+open(COMPOSE_TEMP, ">compose.TEMP");
+open(COMPOSE_SP_TEMP, ">compose_sp.TEMP");
+
+while (<UNICODEDATA>) {
     chop;
     (
      $code0,
@@ -33,48 +47,168 @@ while (<UNICODEDATA>){
      $Simple_Uppercase_Mapping12,
      $Simple_Lowercase_Mapping13,
      $Simple_Titlecase_Mapping14
-     ) = split(/\;/);
+    ) = split(/\;/);
 
     if (($Decomposition_Mapping5 ne "") && ($Decomposition_Mapping5 !~ /\</) && ($Decomposition_Mapping5 =~ / /)) {
        ($base, $comb) = split(/ /,$Decomposition_Mapping5);
-       
+
        $leftbracket  = "  { ";
        $rightbracket =" },     ";
 
-       if (hex($code0) > 0xFFFF) {           # DELETE THIS LINE  IF INTERNAL CODE IS UCS4
-           $leftbracket  = "\/\*{ ";         # DELETE THIS LINE  IF INTERNAL CODE IS UCS4
-           $rightbracket =" },\*\/   ";      # DELETE THIS LINE  IF INTERNAL CODE IS UCS4
-       }                                     # DELETE THIS LINE  IF INTERNAL CODE IS UCS4
-       
        # AFP 3.x Spec
        if ( ((0x2000  <= hex($code0)) && (hex($code0) <=  0x2FFF))
-         || ((0xFE30  <= hex($code0)) && (hex($code0) <=  0xFE4F))
-          || ((0x2F800 <= hex($code0)) && (hex($code0) <= 0x2FA1F))) {
+            || ((0xFE30  <= hex($code0)) && (hex($code0) <=  0xFE4F))
+            || ((0x2F800 <= hex($code0)) && (hex($code0) <= 0x2FA1F))) {
+           $leftbracket  = "\/\*{ ";
+           $rightbracket =" },\*\/   ";
+       }
+
+       if (hex($code0) > 0xFFFF) {
+
+           $code0_sp_hi = 0xD800 - (0x10000 >> 10) + (hex($code0) >> 10);
+           $code0_sp_lo = 0xDC00 + (hex($code0) & 0x3FF);
+
+           $base_sp_hi = 0xD800 - (0x10000 >> 10) + (hex($base) >> 10);
+           $base_sp_lo = 0xDC00 + (hex($base) & 0x3FF);
+
+           $comb_sp_hi = 0xD800 - (0x10000 >> 10) + (hex($comb) >> 10);
+           $comb_sp_lo = 0xDC00 + (hex($comb) & 0x3FF);
+
+           printf(COMPOSE_SP_TEMP "%s0x%04X%04X, 0x%04X%04X, 0x%04X%04X%s\/\* %s \*\/\n",
+                  $leftbracket, $code0_sp_hi ,$code0_sp_lo, $base_sp_hi, $base_sp_lo, $comb_sp_hi, $comb_sp_lo, $rightbracket, $Name1);
+
            $leftbracket  = "\/\*{ ";
            $rightbracket =" },\*\/   ";
        }
-       
-       printf(PRECOMPOSETEMP "%s0x%08X, 0x%08X, 0x%08X%s\/\* %s \*\/\n", $leftbracket, hex($code0), hex($base), hex($comb), $rightbracket, $Name1);
 
-       printf( DECOMPOSETEMP "%s0x%08X, 0x%08X, 0x%08X%s\/\* %s \*\/\n", $leftbracket, hex($code0), hex($base), hex($comb), $rightbracket, $Name1);
-       
+       printf(COMPOSE_TEMP "%s0x%08X, 0x%08X, 0x%08X%s\/\* %s \*\/\n", $leftbracket, hex($code0), hex($base), hex($comb), $rightbracket, $Name1);
+
+    }
+}
+
+close(UNICODEDATA);
+
+close(COMPOSE_TEMP);
+close(COMPOSE_SP_TEMP);
+
+# macros for BMP (PRECOMP_COUNT, DECOMP_COUNT, MAXCOMBLEN) ----------------
+
+open(COMPOSE_TEMP, "<compose.TEMP");
+
+@comp_table = ();
+$comp_count = 0;
+
+while (<COMPOSE_TEMP>) {
+    if (m/^\/\*/) {
+       next;
+    }
+    $comp_table[$comp_count][0] = substr($_, 4, 10);
+    $comp_table[$comp_count][1] = substr($_, 16, 10);
+    $comp_count++;
+}
+
+$maxcomblen = 2;      # Hangul's maxcomblen is already 2. That is, VT.
+
+for ($i = 0 ; $i < $comp_count ; $i++) {
+    $base = $comp_table[$i][1];
+    $comblen = 1;
+    $j = 0;
+    while ($j < $comp_count) {
+       if ($base ne $comp_table[$j][0]) {
+           $j++;
+           next;
+       } else {
+           $comblen++;
+           $base =  $comp_table[$j][1];
+           $j = 0;
+       }
+    }
+    $maxcomblen = ($maxcomblen > $comblen) ? $maxcomblen : $comblen;
+}
+
+close(COMPOSE_TEMP);
+
+# macros for SP (PRECOMP_SP_COUNT,DECOMP_SP_COUNT, MAXCOMBSPLEN) -----------
+
+open(COMPOSE_SP_TEMP, "<compose_sp.TEMP");
+
+@comp_sp_table = ();
+$comp_sp_count = 0;
+
+while (<COMPOSE_SP_TEMP>) {
+    if (m/^\/\*/) {
+       next;
+    }
+    $comp_sp_table[$comp_sp_count][0] = substr($_, 4, 10);
+    $comp_sp_table[$comp_sp_count][1] = substr($_, 16, 10);
+    $comp_sp_count++;
+}
+
+$maxcombsplen = 2;     # one char have 2 codepoints, like a D8xx DCxx.
+
+for ($i = 0 ; $i < $comp_sp_count ; $i++) {
+    $base_sp = $comp_sp_table[$i][1];
+    $comblen = 2;
+    $j = 0;
+    while ($j < $comp_sp_count) {
+       if ($base_sp ne $comp_sp_table[$j][0]) {
+           $j++;
+           next;
+       } else {
+           $comblen += 2;
+           $base_sp =  $comp_sp_table[$j][1];
+           $j = 0;
+       }
     }
+    $maxcombsplen = ($maxcombsplen > $comblen) ? $maxcombsplen : $comblen;
 }
 
+close(COMPOSE_SP_TEMP);
+
+# macro for buffer length (COMBBUFLEN) -------------------------------------
+
+$combbuflen = ($maxcomblen > $maxcombsplen) ? $maxcomblen : $maxcombsplen;
+
 # sort ---------------------------------------------------------------------
 
-system("sort -k 3 precompose.TEMP \> precompose.SORT");
-system("sort -k 2  decompose.TEMP \>  decompose.SORT");
+system("sort -k 3 compose.TEMP \> precompose.SORT");
+system("sort -k 2 compose.TEMP \>  decompose.SORT");
+
+system("sort -k 3 compose_sp.TEMP \> precompose_sp.SORT");
+system("sort -k 2 compose_sp.TEMP \>  decompose_sp.SORT");
 
 # print  -------------------------------------------------------------------
 
-printf ("\/\* This file is generated by contrib/misc/make-precompose.h.pl %s \*\/\n", $ARGV[0]);
 print ("\/\* DO NOT EDIT BY HAND\!\!\!                                           \*\/\n");
+print ("\/\* This file is generated by                                        \*\/\n");
+printf ("\/\*              contrib/misc/make-precompose.h.pl %s   \*\/\n", $ARGV[0]);
 print ("\n");
 printf ("\/\* %s is got from                                      \*\/\n", $ARGV[0]);
 print ("\/\* http\:\/\/www.unicode.org\/Public\/UNIDATA\/UnicodeData.txt            \*\/\n");
 print ("\n");
 
+print ("\#define SBASE 0xAC00\n");
+print ("\#define LBASE 0x1100\n");
+print ("\#define VBASE 0x1161\n");
+print ("\#define TBASE 0x11A7\n");
+print ("\#define LCOUNT 19\n");
+print ("\#define VCOUNT 21\n");
+print ("\#define TCOUNT 28\n");
+print ("\#define NCOUNT 588     \/\* (VCOUNT \* TCOUNT) \*\/\n");
+print ("\#define SCOUNT 11172   \/\* (LCOUNT \* NCOUNT) \*\/\n");
+print ("\n");
+
+printf ("\#define PRECOMP_COUNT %d\n", $comp_count);
+printf ("\#define DECOMP_COUNT %d\n", $comp_count);
+printf ("\#define MAXCOMBLEN %d\n", $maxcomblen);
+print ("\n");
+printf ("\#define PRECOMP_SP_COUNT %d\n", $comp_sp_count);
+printf ("\#define DECOMP_SP_COUNT %d\n", $comp_sp_count);
+printf ("\#define MAXCOMBSPLEN %d\n", $maxcombsplen);
+print ("\n");
+printf ("\#define COMBBUFLEN %d  \/\* max\(MAXCOMBLEN\,MAXCOMBSPLEN\) \*\/\n", $combbuflen);
+print ("\n");
+
 print ("static const struct \{\n");
 print ("  unsigned int replacement\;\n");
 print ("  unsigned int base\;\n");
@@ -97,6 +231,30 @@ system("cat decompose.SORT");
 print ("\}\;\n");
 print ("\n");
 
+
+
+print ("static const struct \{\n");
+print ("  unsigned int replacement_sp\;\n");
+print ("  unsigned int base_sp\;\n");
+print ("  unsigned int comb_sp\;\n");
+print ("\} precompositions_sp\[\] \= \{\n");
+
+system("cat precompose_sp.SORT");
+
+print ("\}\;\n");
+print ("\n");
+
+print ("static const struct \{\n");
+print ("  unsigned int replacement_sp\;\n");
+print ("  unsigned int base_sp\;\n");
+print ("  unsigned int comb_sp\;\n");
+print ("\} decompositions_sp\[\] \= \{\n");
+
+system("cat decompose_sp.SORT");
+
+print ("\}\;\n");
+print ("\n");
+
 print ("\/\* EOF \*\/\n");
 
 # EOF
diff --git a/contrib/patches/README b/contrib/patches/README
deleted file mode 100644 (file)
index 731933b..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-This directory contains patches that are under consideration or being
-tested.
diff --git a/contrib/patches/patch.afp_vfs b/contrib/patches/patch.afp_vfs
deleted file mode 100644 (file)
index f2c08ee..0000000
+++ /dev/null
@@ -1,1678 +0,0 @@
-First try for a netatalk vfs layer
-
-current schemes
-adouble=v1,v2 classic adouble format
-adouble=osx  ._<filename> OSX resource fork.
-adouble=ads  NT like alternate data stream. 
-
-Note for ads:
-* cf. patch.vfs for samba ADS vfs layer and patch.samba.xx for samba tree patch.
-
-* It's using Afp_AfpInfo name (MS SFM name) but it's not yet compatible with SFM.
-  from cdrecord source code Afp_AfpInfo is the raw HFS data, we are storing an appledouble file.
-
-* Server side copy and Macintosh copy only deal with resource fork, other NT ADS are lost.
-  unfixable for Macintosh copy but doable for server side.
-
-* It's ok for rename, delete, chown and chmod.
-
-* Copy from a NT box should work.
-
-* Last but not least : only on a new volume!
-
-TODO
-indirection for metadata, aka stored in EA, a different file, whatever.
-
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/Makefile.am ./etc/afpd/Makefile.am
---- ../src.dev2/etc/afpd/Makefile.am   Mon Feb  9 22:45:51 2004
-+++ ./etc/afpd/Makefile.am     Fri Jun 18 19:15:47 2004
-@@ -8,14 +8,14 @@
-        file.c enumerate.c desktop.c filedir.c fork.c appl.c gettok.c \
-        mangle.c status.c afp_options.c afp_asp.c afp_dsi.c messages.c  \
-        afp_config.c nfsquota.c quota.c uam.c afs.c uid.c afp_util.c \
--         catsearch.c afprun.c
-+         catsearch.c afprun.c vfs_adouble.c
- afpd_LDADD =  $(top_builddir)/libatalk/cnid/libcnid.la $(top_builddir)/libatalk/libatalk.la
- afpd_LDFLAGS = -export-dynamic
- noinst_HEADERS = auth.h afp_config.h desktop.h directory.h file.h \
-        filedir.h fork.h globals.h icon.h mangle.h misc.h status.h switch.h \
--       uam_auth.h uid.h unix.h volume.h
-+       uam_auth.h uid.h unix.h volume.h afp_vfs.h
- LIBS = @LIBS@ @PAM_LIBS@ @QUOTA_LIBS@ @SLP_LIBS@
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/afp_vfs.h ./etc/afpd/afp_vfs.h
---- ../src.dev2/etc/afpd/afp_vfs.h     Thu Jan  1 00:00:00 1970
-+++ ./etc/afpd/afp_vfs.h       Wed Jun 23 03:56:15 2004
-@@ -0,0 +1,49 @@
-+/*
-+   Copyright (c) 2004 Didier Gautheron
-+ 
-+   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., 675 Mass Ave, Cambridge, MA 02139, USA.
-+
-+   vfs layer for afp
-+*/
-+
-+#ifndef _AFP_VFS_H
-+#define _AFP_VFS_H
-+
-+#include <atalk/adouble.h>
-+struct vol;
-+
-+struct vfs_ops {
-+    /* low level adouble fn */
-+    char *(*ad_path)(const char *, int);
-+
-+    /* */
-+    int (*validupath)(const struct vol *, const char *);
-+    int (*rf_chown)(const struct vol *, const char *path, uid_t owner, gid_t group);
-+    int (*rf_renamedir)(const struct vol *, const char *oldpath, const char *newpath);
-+    int (*rf_deletecurdir)(const struct vol *);
-+    int (*rf_setfilmode)(const struct vol *, const char * name, mode_t mode, struct stat *st);
-+    int (*rf_setdirmode)(const struct vol *, const char * name, mode_t mode, struct stat *st);
-+    int (*rf_setdirunixmode)(const struct vol *, const char * name, mode_t mode, struct stat *st);
-+
-+    int (*rf_setdirowner)(const struct vol *, const char *path, uid_t owner, gid_t group);
-+
-+    int (*rf_deletefile)(const struct vol *, const char * );
-+    int (*rf_renamefile)(const struct vol *, const char *oldpath, const char *newpath);
-+
-+};
-+
-+void initvol_vfs(struct vol *vol);
-+
-+#endif
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/directory.c ./etc/afpd/directory.c
---- ../src.dev2/etc/afpd/directory.c   Mon Jul 12 08:46:03 2004
-+++ ./etc/afpd/directory.c     Sat Jun 19 14:07:14 2004
-@@ -610,7 +610,7 @@
-    system rmdir with afp error code.
-    ENOENT is not an error.
-  */
--static int netatalk_rmdir(const char *name)
-+int netatalk_rmdir(const char *name)
- {
-     if (rmdir(name) < 0) {
-         switch ( errno ) {
-@@ -2075,15 +2075,11 @@
-         }
-     }
--    if (vol->v_adouble == AD_VERSION2_OSX) {
--        /* We simply move the corresponding ad file as well */
--        char   tempbuf[258]="._";
--        rename(vol->ad_path(src,0),strcat(tempbuf,dst));
--    }
-+    vol->vfs->rf_renamedir(vol, src, dst);
-     len = strlen( newname );
-     /* rename() succeeded so we need to update our tree even if we can't open
--     * .Parent
-+     * metadata
-     */
-     
-     ad_init(&ad, vol->v_adouble);
-@@ -2132,12 +2128,9 @@
-     return( AFP_OK );
- }
--#define DOT_APPLEDOUBLE_LEN 13
- /* delete an empty directory */
--int deletecurdir( vol, path, pathlen )
-+int deletecurdir( vol)
- const struct vol      *vol;
--char *path;
--int pathlen;
- {
-     struct dirent *de;
-     struct stat st;
-@@ -2162,42 +2155,9 @@
-             return  AFPERR_OLOCK;
-         }
-     }
--
--    if (vol->v_adouble == AD_VERSION2_OSX) {
--       
--        if ((err = netatalk_unlink(vol->ad_path(".",0) )) ) {
--            return err;
--        }
--    }
--    else {
--        /* delete stray .AppleDouble files. this happens to get .Parent files
--           as well. */
--        if ((dp = opendir(".AppleDouble"))) {
--            strcpy(path, ".AppleDouble/");
--            while ((de = readdir(dp))) {
--                /* skip this and previous directory */
--                if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
--                    continue;
--
--                /* bail if the file exists in the current directory.
--                 * note: this will not fail with dangling symlinks */
--                if (stat(de->d_name, &st) == 0) {
--                    closedir(dp);
--                    return AFPERR_DIRNEMPT;
--                }
--
--                strcpy(path + DOT_APPLEDOUBLE_LEN, de->d_name);
--                if ((err = netatalk_unlink(path))) {
--                    closedir(dp);
--                    return err;
--                }
--            }
--            closedir(dp);
--        }
--
--        if ( (err = netatalk_rmdir( ".AppleDouble" ))  ) {
--            return err;
--        }
-+    err = vol->vfs->rf_deletecurdir(vol);
-+    if (err) {
-+        return err;
-     }
-     /* now get rid of dangling symlinks */
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/directory.h ./etc/afpd/directory.h
---- ../src.dev2/etc/afpd/directory.h   Mon May 10 18:40:32 2004
-+++ ./etc/afpd/directory.h     Sat Jun 19 03:23:18 2004
-@@ -196,7 +196,7 @@
- extern struct dir       *dirinsert __P((struct vol *, struct dir *));
- extern int              movecwd __P((const struct vol *, struct dir *));
--extern int              deletecurdir __P((const struct vol *, char *, int));
-+extern int              deletecurdir __P((const struct vol *));
- extern struct path      *cname __P((const struct vol *, struct dir *,
-                              char **));
- extern mode_t           mtoumode __P((struct maccess *));
-@@ -215,6 +215,7 @@
- extern int  check_access __P((char *name , int mode));
- extern int file_access   __P((struct path *path, int mode));
-+extern int netatalk_rmdir __P((const char *name));
- extern int netatalk_unlink __P((const char *name));
- /* from enumerate.c */
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/enumerate.c ./etc/afpd/enumerate.c
---- ../src.dev2/etc/afpd/enumerate.c   Mon Jul 12 08:46:03 2004
-+++ ./etc/afpd/enumerate.c     Thu Jun 24 04:26:35 2004
-@@ -166,7 +166,7 @@
-     if (!strcmp(name, "..") || !strcmp(name, "."))
-         return NULL;
--    if (!vol->validupath(vol, name))
-+    if (!vol->vfs->validupath(vol, name))
-         return NULL;
-     /* check for vetoed filenames */
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/file.c ./etc/afpd/file.c
---- ../src.dev2/etc/afpd/file.c        Tue Jun 15 22:53:54 2004
-+++ ./etc/afpd/file.c  Mon Jun 21 00:21:24 2004
-@@ -901,7 +901,7 @@
-     /* second try with adouble open 
-     */
--    if (ad_open( upath, vol_noadouble(vol) | ADFLAGS_HF,
-+    if ( ad_open( upath, vol_noadouble(vol) | ADFLAGS_HF,
-                  O_RDWR|O_CREAT, 0666, adp) < 0) {
-         /* for some things, we don't need an adouble header */
-         if (f_bitmap & ~(1<<FILPBIT_MDATE)) {
-@@ -1020,7 +1020,6 @@
- char  *src, *dst, *newname;
- struct adouble    *adp;
- {
--    char      adsrc[ MAXPATHLEN + 1];
-     int               rc;
- #ifdef DEBUG
-@@ -1055,38 +1054,10 @@
-         }
-     }
--    strcpy( adsrc, vol->ad_path( src, 0 ));
--
--    if (unix_rename( adsrc, vol->ad_path( dst, 0 )) < 0 ) {
--        struct stat st;
-+    if (vol->vfs->rf_renamefile(vol, src, dst) < 0 ) {
-         int err;
-         
-         err = errno;        
--      if (errno == ENOENT) {
--          struct adouble    ad;
--
--            if (stat(adsrc, &st)) /* source has no ressource fork, */
--                return AFP_OK;
--            
--            /* We are here  because :
--             * -there's no dest folder. 
--             * -there's no .AppleDouble in the dest folder.
--             * if we use the struct adouble passed in parameter it will not
--             * create .AppleDouble if the file is already opened, so we
--             * use a diff one, it's not a pb,ie it's not the same file, yet.
--             */
--            ad_init(&ad, vol->v_adouble); 
--            if (!ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) {
--              ad_close(&ad, ADFLAGS_HF);
--              if (!unix_rename( adsrc, vol->ad_path( dst, 0 )) ) 
--                   err = 0;
--                else 
--                   err = errno;
--            }
--            else { /* it's something else, bail out */
--              err = errno;
--          }
--      }
-       /* try to undo the data fork rename,
-        * we know we are on the same device 
-       */
-@@ -1436,6 +1407,7 @@
-     if (ret_err) {
-         deletefile(d_vol, dst, 0);
-     }
-+    /* ADS here */
-     /* set dest modification date to src date */
-     if (!stat(src, &st)) {
-@@ -1562,14 +1534,12 @@
-     if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
-         err = AFPERR_BUSY;
-     }
--    else if (!(err = netatalk_unlink( vol->ad_path( file, ADFLAGS_HF)) ) &&
--             !(err = netatalk_unlink( file )) ) {
-+    else if (!(err = vol->vfs->rf_deletefile(vol, file)) && !(err = netatalk_unlink( file )) ) {
-         cnid_t id;
-         if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) 
-         {
-             cnid_delete(vol->v_cdb, id);
-         }
--
-     }
-     if (adp)
-         ad_close( &ad, adflags );  /* ad_close removes locks if any */
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/filedir.c ./etc/afpd/filedir.c
---- ../src.dev2/etc/afpd/filedir.c     Mon May 10 18:40:32 2004
-+++ ./etc/afpd/filedir.c       Sat Jun 19 15:09:08 2004
-@@ -73,7 +73,7 @@
-         return AFPERR_NOOBJ ;
-     }
--    adpath = vol->ad_path( upath, ADFLAGS_HF );
-+    adpath = vol->vfs->ad_path( upath, ADFLAGS_HF );
-     /* FIXME dirsearch doesn't move cwd to did ! */
-     if (( dir = dirlookup( vol, did )) == NULL ) {
-         LOG(log_error, logtype_afpd, "matchfile2dirperms: Unable to get directory info.");
-@@ -313,7 +313,7 @@
-     if ((vol->v_flags & AFPVOL_NOHEX) && strchr(name, '/'))
-         return AFPERR_PARAM;
--    if (!vol->validupath(vol, name))
-+    if (!vol->vfs->validupath(vol, name))
-         return AFPERR_EXIST;
-     /* check for vetoed filenames */
-@@ -582,7 +582,7 @@
-           rc = AFPERR_ACCESS;
-       }
-       else {
--            rc = deletecurdir( vol, obj->oldtmp, AFPOBJ_TMPSIZ);
-+            rc = deletecurdir( vol);
-         }
-     } else if (of_findname(s_path)) {
-         rc = AFPERR_BUSY;
-@@ -764,7 +764,7 @@
-                 int  admode = ad_mode("", 0777);
-                 setfilmode(upath, admode, NULL);
--                setfilmode(vol->ad_path( upath, ADFLAGS_HF ), ad_hf_mode(admode), NULL);
-+                vol->vfs->rf_setfilmode(vol, upath, admode, NULL);
-             }
-         setvoltime(obj, vol );
-     }
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/unix.c ./etc/afpd/unix.c
---- ../src.dev2/etc/afpd/unix.c        Tue Jun 15 22:53:55 2004
-+++ ./etc/afpd/unix.c  Wed Jun 23 04:04:01 2004
-@@ -260,8 +260,8 @@
-    rwx-wx-wx or rwx-wx-- 
-    rwx----wx (is not asked by a Mac with OS >= 8.0 ?)
- */
--static int stickydirmode(name, mode, dropbox)
--char * name;
-+int stickydirmode(name, mode, dropbox)
-+const char * name;
- const mode_t mode;
- const int dropbox;
- {
-@@ -405,12 +405,12 @@
-     if (setfilmode( path->u_name, mode, &path->st) < 0)
-         return -1;
-     /* we need to set write perm if read set for resource fork */
--    return setfilmode(vol->ad_path( path->u_name, ADFLAGS_HF ), ad_hf_mode(mode), &path->st);
-+    return vol->vfs->rf_setfilmode(vol, path->u_name, mode, &path->st);
- }
- /* --------------------- */
- int setfilmode(name, mode, st)
--char * name;
-+const char * name;
- mode_t mode;
- struct stat *st;
- {
-@@ -436,29 +436,18 @@
- const char       *name;
- const mode_t     mode;
- {
--char *adouble = vol->ad_path( name, ADFLAGS_DIR );
-     int dropbox = (vol->v_flags & AFPVOL_DROPBOX);
-     if (dir_rx_set(mode)) {
--      /* extending right? dir first then .AppleDouble */
-+      /* extending right? dir first then .AppleDouble in rf_setdirmode */
-       if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
-               return -1;
--      if (vol->v_adouble != AD_VERSION2_OSX) {
--            if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) {
--                return  -1 ;
--            }
--        }
-     }
--    if (setfilmode(adouble, ad_hf_mode(mode), NULL) < 0 && !vol_noadouble(vol)) {
-+    if (vol->vfs->rf_setdirunixmode(vol, name, mode, NULL) < 0 && !vol_noadouble(vol)) {
-         return  -1 ;
-     }
-     if (!dir_rx_set(mode)) {
--      if (vol->v_adouble != AD_VERSION2_OSX) {
--            if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) {
--                return  -1 ;
--            }
--        }
-       if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
-             return -1;
-     }
-@@ -471,26 +460,17 @@
- const char       *name;
- const mode_t mode;
- {
--    char              buf[ MAXPATHLEN + 1];
-     struct stat               st;
--    char              *m;
-     struct dirent     *dirp;
-     DIR                       *dir;
-     int                 osx = vol->v_adouble == AD_VERSION2_OSX;
-     int                 hf_mode = ad_hf_mode(mode);
-     int                 dropbox = (vol->v_flags & AFPVOL_DROPBOX);
--    char                *adouble = vol->ad_path( name, ADFLAGS_DIR );
--    char                *adouble_p = ad_dir(adouble);
-     
-     if (dir_rx_set(mode)) {
--      /* extending right? dir first then .AppleDouble */
-+      /* extending right? dir first */
-       if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
-               return -1;
--      if (!osx) {
--            if (stickydirmode(adouble_p, DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) {
--                return  -1 ;
--            }
--        }
-     }
-     
-     if (( dir = opendir( name )) == NULL ) {
-@@ -516,61 +496,13 @@
-                 return -1;
-            }
-         }
--#if 0
--        /* Don't change subdir perm */
--        else if (S_ISDIR(st.st_mode)) {
--                if (stickydirmode(dirp->d_name, DIRBITS | mode, dropbox) < 0)
--                    return (-1);
--            } else if (stickydirmode(dirp->d_name, mode, dropbox) < 0)
--                return (-1);
--        }
--#endif
-     }
-     closedir( dir );
-     
--    if (osx) {
--        goto setdirmode_noadouble;
--    }
--    
--    /* change perm of .AppleDouble's files
--    */
--    if (( dir = opendir( adouble_p )) == NULL ) {
--        if (vol_noadouble(vol))
--            goto setdirmode_noadouble;
--        LOG(log_error, logtype_afpd, "setdirmode: opendir %s: %s", fullpathname(".AppleDouble"),strerror(errno) );
--        return( -1 );
--    }
--    strcpy( buf, adouble_p);
--    strcat( buf, "/" );
--    m = strchr( buf, '\0' );
--    for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
--        if ( strcmp( dirp->d_name, "." ) == 0 ||
--                strcmp( dirp->d_name, ".." ) == 0 ) {
--            continue;
--        }
--        *m = '\0';
--        strcat( buf, dirp->d_name );
--
--        if ( stat( buf, &st ) < 0 ) {
--            LOG(log_error, logtype_afpd, "setdirmode: stat %s: %s", buf, strerror(errno) );
--            continue;
--        }
--        if (!S_ISDIR(st.st_mode)) {
--           if (setfilmode(buf, hf_mode , &st) < 0) {
--               /* FIXME what do we do then? */
--           }
--        }
--    } /* end for */
--    closedir( dir );
--
--    if (!dir_rx_set(mode)) {
--        /* XXX: need to preserve special modes */
--        if (stickydirmode(adouble_p, DIRBITS | mode, dropbox) < 0 ) {
--                return  -1 ;
--        }
-+    if (vol->vfs->rf_setdirmode(vol, name, mode, NULL) < 0 && !vol_noadouble(vol)) {
-+        return  -1 ;
-     }
--setdirmode_noadouble:
-     if (!dir_rx_set(mode)) {
-       if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
-               return -1;
-@@ -578,6 +510,7 @@
-     return( 0 );
- }
-+/* ----------------------------- */
- int setdeskowner( uid, gid )
- const uid_t   uid;
- const gid_t   gid;
-@@ -648,8 +581,6 @@
- const gid_t   gid;
- struct path* path;
- {
--    struct stat st;
--    char  *ad_p;
-     if (!path->st_valid) {
-         of_stat(path);
-@@ -665,22 +596,15 @@
-       return -1;
-     }
--    ad_p = vol->ad_path( path->u_name, ADFLAGS_HF );
--
--    if ( stat( ad_p, &st ) < 0 ) {
--      /* ignore */
--        return 0;
--    }
--    if ( chown( ad_p, uid, gid ) < 0 &&
--            errno != EPERM ) {
--        LOG(log_debug, logtype_afpd, "setfilowner: chown %d/%d %s: %s",
--            uid, gid, ad_p, strerror(errno) );
-+    if (vol->vfs->rf_chown(vol, path->u_name, uid, gid ) < 0 && errno != EPERM) {
-+        LOG(log_debug, logtype_afpd, "setfilowner: rf_chown %d/%d %s: %s",
-+            uid, gid, path->u_name, strerror(errno) );
-         return -1;
-     }
-+
-     return 0;
- }
--
- /* --------------------------------- 
-  * uid/gid == 0 need to be handled as special cases. they really mean
-  * that user/group should inherit from other, but that doesn't fit
-@@ -692,15 +616,10 @@
- const uid_t   uid;
- const gid_t   gid;
- {
--    char              buf[ MAXPATHLEN + 1];
-     struct stat               st;
--    char              *m;
-     struct dirent     *dirp;
-     DIR                       *dir;
-     int                 osx = vol->v_adouble == AD_VERSION2_OSX;
--    int                 noadouble = vol_noadouble(vol);
--    char                *adouble; 
--    char                *adouble_p;
-     if (( dir = opendir( name )) == NULL ) {
-         return( -1 );
-@@ -723,56 +642,15 @@
-         }
-     }
-     closedir( dir );
--    
--    if (osx) {
--       goto setdirowner_noadouble;
--    }
--    adouble = vol->ad_path( name, ADFLAGS_DIR );
--    adouble_p = ad_dir(adouble);
--    if (( dir = opendir( adouble_p )) == NULL ) {
--        if (noadouble)
--            goto setdirowner_noadouble;
--        return( -1 );
--    }
--    strcpy( buf, adouble_p );
--    strcat( buf, "/" );
--    m = strchr( buf, '\0' );
--    for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
--        if ( strcmp( dirp->d_name, "." ) == 0 ||
--                strcmp( dirp->d_name, ".." ) == 0 ) {
--            continue;
--        }
--        *m = '\0';
--        strcat( buf, dirp->d_name );
--        if ( chown( buf, uid, gid ) < 0 && errno != EPERM ) {
--            LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
--                uid, gid, fullpathname(buf), strerror(errno) );
--            /* return ( -1 ); Sometimes this is okay */
--        }
--    }
--    closedir( dir );
--
--    /*
--     * We cheat: we know that chown doesn't do anything.
--     */
--    if ( stat( ".AppleDouble", &st ) < 0 ) {
--        LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s", fullpathname(".AppleDouble"), strerror(errno) );
--        return( -1 );
--    }
--    if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 &&
--            errno != EPERM ) {
--        LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
--            uid, gid,fullpathname(".AppleDouble"), strerror(errno) );
--        /* return ( -1 ); Sometimes this is okay */
-+    if (vol->vfs->rf_setdirowner(vol, name, uid, gid) < 0) {
-+        return -1;
-     }
--
--setdirowner_noadouble:
-+    
-     if ( stat( ".", &st ) < 0 ) {
-         return( -1 );
-     }
--    if ( gid && gid != st.st_gid && chown( ".", uid, gid ) < 0 &&
--            errno != EPERM ) {
-+    if ( gid && gid != st.st_gid && chown( ".", uid, gid ) < 0 && errno != EPERM ) {
-         LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
-             uid, gid, fullpathname("."), strerror(errno) );
-     }
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/unix.h ./etc/afpd/unix.h
---- ../src.dev2/etc/afpd/unix.h        Mon May 10 18:40:33 2004
-+++ ./etc/afpd/unix.h  Wed Jun 23 03:43:28 2004
-@@ -221,11 +221,12 @@
- extern int setdirmode       __P((const struct vol *, const char *, const mode_t));
- extern int setdeskowner     __P((const uid_t, const gid_t));
- extern int setdirowner      __P((const struct vol *, const char *, const uid_t, const gid_t));
--extern int setfilmode       __P((char *, mode_t , struct stat *));
-+extern int setfilmode       __P((const char *, mode_t , struct stat *));
- extern int setfilunixmode   __P((const struct vol *, struct path*, const mode_t));
- extern int setfilowner      __P((const struct vol *, const uid_t, const gid_t, struct path*));
- extern int unix_rename      __P((const char *oldpath, const char *newpath));
- extern int dir_rx_set       __P((mode_t mode));
-+extern int stickydirmode    __P((const char * name, const mode_t mode, const int dropbox));
- extern void accessmode      __P((char *, struct maccess *, struct dir *, struct stat *));
- extern char *fullpathname   __P((const char *));
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/vfs_adouble.c ./etc/afpd/vfs_adouble.c
---- ../src.dev2/etc/afpd/vfs_adouble.c Thu Jan  1 00:00:00 1970
-+++ ./etc/afpd/vfs_adouble.c   Wed Jun 30 19:31:49 2004
-@@ -0,0 +1,749 @@
-+/*
-+    Copyright (c) 2004 Didier Gautheron
-+ 
-+   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., 675 Mass Ave, Cambridge, MA 02139, USA.
-+ 
-+*/
-+#ifdef HAVE_CONFIG_H
-+#include "config.h"
-+#endif /* HAVE_CONFIG_H */
-+
-+#ifdef STDC_HEADERS
-+#include <string.h>
-+#endif
-+
-+#include <stdio.h>
-+    
-+#include <atalk/adouble.h>
-+#include <atalk/logger.h>
-+#include <atalk/util.h>
-+
-+#include "directory.h"
-+#include "volume.h"
-+#include "unix.h"
-+
-+struct perm {
-+    uid_t uid;
-+    gid_t gid;
-+};
-+
-+typedef int (*rf_loop)(struct dirent *, char *, void *, int );
-+
-+/* ----------------------------- */
-+static int 
-+for_each_adouble(const char *from, const char *name, rf_loop fn, void *data, int flag)
-+{
-+    char            buf[ MAXPATHLEN + 1];
-+    char            *m;
-+    DIR             *dp;
-+    struct dirent   *de;
-+    int             ret;
-+    
-+
-+    if (NULL == ( dp = opendir( name)) ) {
-+        if (!flag) {
-+            LOG(log_error, logtype_afpd, "%s: opendir %s: %s", from, fullpathname(name),strerror(errno) );
-+            return -1;
-+        }
-+        return 0;
-+    }
-+    strlcpy( buf, name, sizeof(buf));
-+    strlcat( buf, "/", sizeof(buf) );
-+    m = strchr( buf, '\0' );
-+    ret = 0;
-+    while ((de = readdir(dp))) {
-+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
-+                continue;
-+        }
-+        
-+        strlcat(buf, de->d_name, sizeof(buf));
-+        if (fn && (ret = fn(de, buf, data, flag))) {
-+           closedir(dp);
-+           return ret;
-+        }
-+        *m = 0;
-+    }
-+    closedir(dp);
-+    return ret;
-+}
-+
-+/* ------------------------------ */
-+static int ads_chown_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+    struct perm   *owner  = data;
-+    
-+    if (chown( name , owner->uid, owner->gid ) < 0) {
-+        return -1;
-+    }
-+    return 0;
-+}
-+
-+static int RF_chown_ads(const struct vol *vol, const char *path, uid_t uid, gid_t gid)
-+
-+{
-+    struct        stat st;
-+    char          *ad_p;
-+    struct perm   owner;
-+    
-+    owner.uid = uid;
-+    owner.gid = gid;
-+
-+
-+    ad_p = ad_dir(vol->vfs->ad_path(path, ADFLAGS_HF ));
-+
-+    if ( stat( ad_p, &st ) < 0 ) {
-+      /* ignore */
-+        return 0;
-+    }
-+    
-+    if (chown( ad_p, uid, gid ) < 0) {
-+      return -1;
-+    }
-+    return for_each_adouble("chown_ads", ad_p, ads_chown_loop, &owner, 1);
-+}
-+
-+/* --------------------------------- */
-+static int deletecurdir_ads1_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+    return netatalk_unlink(name);
-+}
-+
-+static int ads_delete_rf(char *name) 
-+{
-+    int err;
-+
-+    if ((err = for_each_adouble("deletecurdir", name, deletecurdir_ads1_loop, NULL, 1))) 
-+        return err;
-+    return netatalk_rmdir(name);
-+}
-+
-+static int deletecurdir_ads_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+    struct stat st;
-+    
-+    /* bail if the file exists in the current directory.
-+     * note: this will not fail with dangling symlinks */
-+    
-+    if (stat(de->d_name, &st) == 0) {
-+        return AFPERR_DIRNEMPT;
-+    }
-+    return ads_delete_rf(name);
-+}
-+
-+static int RF_deletecurdir_ads(const struct vol *vol)
-+{
-+    int err;
-+    
-+    /* delete stray .AppleDouble files. this happens to get .Parent files as well. */
-+    if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_ads_loop, NULL, 1))) 
-+        return err;
-+    return netatalk_rmdir( ".AppleDouble" );
-+}
-+
-+/* ------------------- */
-+struct set_mode {
-+    mode_t mode;
-+    struct stat *st;
-+};
-+
-+static int ads_setfilmode_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+    struct set_mode *param = data;
-+
-+    return setfilmode(name, param->mode, param->st);
-+}
-+
-+static int ads_setfilmode(const char * name, mode_t mode, struct stat *st)
-+{
-+    mode_t dir_mode = mode;
-+    mode_t file_mode = ad_hf_mode(mode);
-+    struct set_mode param;
-+
-+    if ((dir_mode & (S_IRUSR | S_IWUSR )))
-+        dir_mode |= S_IXUSR;
-+    if ((dir_mode & (S_IRGRP | S_IWGRP )))
-+        dir_mode |= S_IXGRP;
-+    if ((dir_mode & (S_IROTH | S_IWOTH )))
-+        dir_mode |= S_IXOTH;  
-+    
-+      /* change folder */
-+      dir_mode |= DIRBITS;
-+    if (dir_rx_set(dir_mode)) {
-+        if (chmod( name,  dir_mode ) < 0)
-+            return -1;
-+    }
-+    param.st = st;
-+    param.mode = file_mode;
-+    if (for_each_adouble("setfilmode_ads", name, ads_setfilmode_loop, &param, 0) < 0)
-+        return -1;
-+
-+    if (!dir_rx_set(dir_mode)) {
-+        if (chmod( name,  dir_mode ) < 0)
-+            return -1;
-+    }
-+
-+    return 0;
-+}
-+
-+static int RF_setfilmode_ads(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
-+{
-+    return ads_setfilmode(ad_dir(vol->vfs->ad_path( name, ADFLAGS_HF )), mode, st);
-+}
-+
-+/* ------------------- */
-+static int RF_setdirunixmode_ads(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
-+{
-+    char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
-+    char   ad_p[ MAXPATHLEN + 1];
-+    int dropbox = (vol->v_flags & AFPVOL_DROPBOX);
-+
-+    strlcpy(ad_p,ad_dir(adouble), MAXPATHLEN + 1);
-+
-+    if (dir_rx_set(mode)) {
-+
-+        /* .AppleDouble */
-+        if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
-+            return -1;
-+
-+        /* .AppleDouble/.Parent */
-+        if (stickydirmode(ad_p, DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
-+            return -1;
-+    }
-+
-+    if (ads_setfilmode(ad_dir(vol->vfs->ad_path( name, ADFLAGS_DIR)), mode, st) < 0)
-+        return -1;
-+
-+    if (!dir_rx_set(mode)) {
-+        if (stickydirmode(ad_p, DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
-+            return  -1 ;
-+        if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
-+            return -1;
-+    }
-+    return 0;
-+}
-+
-+/* ------------------- */
-+struct dir_mode {
-+    mode_t mode;
-+    int    dropbox;
-+};
-+
-+static int setdirmode_ads_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+
-+    struct dir_mode *param = data;
-+    int    ret = 0; /* 0 ignore error, -1 */
-+
-+    if (dir_rx_set(param->mode)) {
-+        if (stickydirmode(name, DIRBITS | param->mode, param->dropbox) < 0) {
-+            if (flag) {
-+                return 0;
-+            }
-+            return ret;
-+        }
-+    }
-+    if (ads_setfilmode(name, param->mode, NULL) < 0)
-+        return ret;
-+
-+    if (!dir_rx_set(param->mode)) {
-+        if (stickydirmode(name, DIRBITS | param->mode, param->dropbox) < 0) {
-+            if (flag) {
-+                return 0;
-+            }
-+            return ret;
-+        }
-+    }
-+    return 0;
-+}
-+
-+static int RF_setdirmode_ads(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
-+{
-+    char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
-+    char   ad_p[ MAXPATHLEN + 1];
-+    struct dir_mode param;
-+
-+    param.mode = mode;
-+    param.dropbox = (vol->v_flags & AFPVOL_DROPBOX);
-+
-+    strlcpy(ad_p,ad_dir(adouble), sizeof(ad_p));
-+
-+    if (dir_rx_set(mode)) {
-+        /* .AppleDouble */
-+        if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, param.dropbox) < 0 && !vol_noadouble(vol)) 
-+            return -1;
-+    }
-+
-+    if (for_each_adouble("setdirmode_ads", ad_dir(ad_p), setdirmode_ads_loop, &param, vol_noadouble(vol)))
-+        return -1;
-+
-+    if (!dir_rx_set(mode)) {
-+        if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, param.dropbox) < 0 && !vol_noadouble(vol)) 
-+            return -1;
-+    }
-+    return 0;
-+}
-+
-+/* ------------------- */
-+static int setdirowner_ads1_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+    struct perm   *owner  = data;
-+
-+    if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) {
-+         LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
-+                owner->uid, owner->gid, fullpathname(name), strerror(errno) );
-+         /* return ( -1 ); Sometimes this is okay */
-+    }
-+    return 0;
-+}
-+
-+static int setdirowner_ads_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+    struct perm   *owner  = data;
-+
-+    if (for_each_adouble("setdirowner", name, setdirowner_ads1_loop, data, flag) < 0)
-+        return -1;
-+
-+    if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) {
-+         LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
-+                owner->uid, owner->gid, fullpathname(name), strerror(errno) );
-+         /* return ( -1 ); Sometimes this is okay */
-+    }
-+    return 0;
-+}
-+
-+static int RF_setdirowner_ads(const struct vol *vol, const char *name, uid_t uid, gid_t gid)
-+{
-+    int           noadouble = vol_noadouble(vol);
-+    char          adouble_p[ MAXPATHLEN + 1];
-+    struct stat   st;
-+    struct perm   owner;
-+    
-+    owner.uid = uid;
-+    owner.gid = gid;
-+
-+    strlcpy(adouble_p, ad_dir(vol->vfs->ad_path( name, ADFLAGS_DIR )), sizeof(adouble_p));
-+
-+    if (for_each_adouble("setdirowner", ad_dir(adouble_p), setdirowner_ads_loop, &owner, noadouble)) 
-+        return -1;
-+
-+    /*
-+     * We cheat: we know that chown doesn't do anything.
-+     */
-+    if ( stat( ".AppleDouble", &st ) < 0) {
-+        if (errno == ENOENT && noadouble)
-+            return 0;
-+        LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s", fullpathname(".AppleDouble"), strerror(errno) );
-+        return -1;
-+    }
-+    if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 && errno != EPERM ) {
-+        LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
-+            uid, gid,fullpathname(".AppleDouble"), strerror(errno) );
-+        /* return ( -1 ); Sometimes this is okay */
-+    }
-+    return 0;
-+}
-+
-+/* ------------------- */
-+static int RF_deletefile_ads(const struct vol *vol, const char *file )
-+{
-+    char *ad_p = ad_dir(vol->vfs->ad_path(file, ADFLAGS_HF ));
-+
-+    return ads_delete_rf(ad_p);
-+}
-+
-+/* --------------------------- */
-+int RF_renamefile_ads(const struct vol *vol, const char *src, const char *dst)
-+{
-+    char  adsrc[ MAXPATHLEN + 1];
-+    int   err = 0;
-+
-+    strcpy( adsrc, ad_dir(vol->vfs->ad_path( src, 0 )));
-+    if (unix_rename( adsrc, ad_dir(vol->vfs->ad_path( dst, 0 ))) < 0) {
-+        struct stat st;
-+
-+        err = errno;
-+        if (errno == ENOENT) {
-+              struct adouble    ad;
-+
-+            if (stat(adsrc, &st)) /* source has no ressource fork, */
-+                return AFP_OK;
-+            
-+            /* We are here  because :
-+             * -there's no dest folder. 
-+             * -there's no .AppleDouble in the dest folder.
-+             * if we use the struct adouble passed in parameter it will not
-+             * create .AppleDouble if the file is already opened, so we
-+             * use a diff one, it's not a pb,ie it's not the same file, yet.
-+             */
-+            ad_init(&ad, vol->v_adouble); 
-+            if (!ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) {
-+              ad_close(&ad, ADFLAGS_HF);
-+
-+              /* We must delete it */
-+              RF_deletefile_ads(vol, dst );
-+              if (!unix_rename( adsrc, ad_dir(vol->vfs->ad_path( dst, 0 ))) ) 
-+                   err = 0;
-+                else 
-+                   err = errno;
-+            }
-+            else { /* it's something else, bail out */
-+                  err = errno;
-+              }
-+          }
-+      }
-+      if (err) {
-+              errno = err;
-+              return -1;
-+      }
-+      return 0;
-+}
-+
-+/* ===================================================
-+ classic adouble format 
-+*/
-+
-+static int validupath_adouble(const struct vol *vol, const char *name) 
-+{
-+    return (vol->v_flags & AFPVOL_USEDOTS) ? strncasecmp(name,".Apple", 6) && strcasecmp(name, ".Parent")
-+                                           : name[0] != '.';
-+}
-+
-+/* ----------------- */
-+static int RF_chown_adouble(const struct vol *vol, const char *path, uid_t uid, gid_t gid)
-+
-+{
-+    struct stat st;
-+    char        *ad_p;
-+
-+    ad_p = vol->vfs->ad_path(path, ADFLAGS_HF );
-+
-+    if ( stat( ad_p, &st ) < 0 )
-+        return 0; /* ignore */
-+
-+    return chown( ad_p, uid, gid );
-+}
-+
-+/* ----------------- */
-+int RF_renamedir_adouble(const struct vol *vol, const char *oldpath, const char *newpath)
-+{
-+    return 0;
-+}
-+
-+/* ----------------- */
-+static int deletecurdir_adouble_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+    struct stat st;
-+    int         err;
-+    
-+    /* bail if the file exists in the current directory.
-+     * note: this will not fail with dangling symlinks */
-+    
-+    if (stat(de->d_name, &st) == 0)
-+        return AFPERR_DIRNEMPT;
-+
-+    if ((err = netatalk_unlink(name)))
-+        return err;
-+
-+    return 0;
-+}
-+
-+static int RF_deletecurdir_adouble(const struct vol *vol)
-+{
-+    int err;
-+
-+    /* delete stray .AppleDouble files. this happens to get .Parent files
-+       as well. */
-+    if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_adouble_loop, NULL, 1))) 
-+        return err;
-+    return netatalk_rmdir( ".AppleDouble" );
-+}
-+
-+/* ----------------- */
-+static int adouble_setfilmode(const char * name, mode_t mode, struct stat *st)
-+{
-+    return setfilmode(name, ad_hf_mode(mode), st);
-+}
-+
-+static int RF_setfilmode_adouble(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
-+{
-+    return adouble_setfilmode(vol->vfs->ad_path( name, ADFLAGS_HF ), mode, st);
-+}
-+
-+/* ----------------- */
-+static int RF_setdirunixmode_adouble(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
-+{
-+    char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
-+    int  dropbox = (vol->v_flags & AFPVOL_DROPBOX);
-+
-+    if (dir_rx_set(mode)) {
-+        if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
-+            return -1;
-+    }
-+
-+    if (adouble_setfilmode(vol->vfs->ad_path( name, ADFLAGS_DIR ), mode, st) < 0) 
-+        return -1;
-+
-+    if (!dir_rx_set(mode)) {
-+        if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
-+            return  -1 ;
-+    }
-+    return 0;
-+}
-+
-+/* ----------------- */
-+static int setdirmode_adouble_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+    int         hf_mode = *(int *)data;
-+    struct stat st;
-+
-+    if ( stat( name, &st ) < 0 ) {
-+        if (flag)
-+            return 0;
-+        LOG(log_error, logtype_afpd, "setdirmode: stat %s: %s", name, strerror(errno) );
-+    }
-+    else if (!S_ISDIR(st.st_mode)) {
-+        if (setfilmode(name, hf_mode , &st) < 0) {
-+               /* FIXME what do we do then? */
-+        }
-+    }
-+    return 0;
-+}
-+
-+static int RF_setdirmode_adouble(const struct vol *vol, const char * name, mode_t mode, struct stat *st1)
-+{
-+    int   dropbox = (vol->v_flags & AFPVOL_DROPBOX);
-+    int   hf_mode = ad_hf_mode(mode);
-+    char  *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
-+    char  *adouble_p = ad_dir(adouble);
-+
-+    if (dir_rx_set(mode)) {
-+        if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
-+            return -1;
-+    }
-+
-+    if (for_each_adouble("setdirmode", adouble_p, setdirmode_adouble_loop, &hf_mode, vol_noadouble(vol)))
-+        return -1;
-+
-+    if (!dir_rx_set(mode)) {
-+        if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
-+            return  -1 ;
-+    }
-+    return 0;
-+}
-+
-+/* ----------------- */
-+static int setdirowner_adouble_loop(struct dirent *de, char *name, void *data, int flag)
-+{
-+    struct perm   *owner  = data;
-+
-+    if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) {
-+         LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
-+                owner->uid, owner->gid, fullpathname(name), strerror(errno) );
-+         /* return ( -1 ); Sometimes this is okay */
-+    }
-+    return 0;
-+}
-+
-+static int RF_setdirowner_adouble(const struct vol *vol, const char *name, uid_t uid, gid_t gid)
-+
-+{
-+    int           noadouble = vol_noadouble(vol);
-+    char          *adouble_p;
-+    struct stat   st;
-+    struct perm   owner;
-+    
-+    owner.uid = uid;
-+    owner.gid = gid;
-+
-+    adouble_p = ad_dir(vol->vfs->ad_path( name, ADFLAGS_DIR ));
-+
-+    if (for_each_adouble("setdirowner", adouble_p, setdirowner_adouble_loop, &owner, noadouble)) 
-+        return -1;
-+
-+    /*
-+     * We cheat: we know that chown doesn't do anything.
-+     */
-+    if ( stat( ".AppleDouble", &st ) < 0) {
-+        if (errno == ENOENT && noadouble)
-+            return 0;
-+        LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s", fullpathname(".AppleDouble"), strerror(errno) );
-+        return -1;
-+    }
-+    if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 && errno != EPERM ) {
-+        LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
-+            uid, gid,fullpathname(".AppleDouble"), strerror(errno) );
-+        /* return ( -1 ); Sometimes this is okay */
-+    }
-+    return 0;
-+}
-+
-+/* ----------------- */
-+static int RF_deletefile_adouble(const struct vol *vol, const char *file )
-+{
-+      return netatalk_unlink(vol->vfs->ad_path( file, ADFLAGS_HF));
-+}
-+
-+/* ----------------- */
-+int RF_renamefile_adouble(const struct vol *vol, const char *src, const char *dst)
-+{
-+    char  adsrc[ MAXPATHLEN + 1];
-+    int   err = 0;
-+
-+    strcpy( adsrc, vol->vfs->ad_path( src, 0 ));
-+    if (unix_rename( adsrc, vol->vfs->ad_path( dst, 0 )) < 0) {
-+        struct stat st;
-+
-+        err = errno;
-+        if (errno == ENOENT) {
-+              struct adouble    ad;
-+
-+            if (stat(adsrc, &st)) /* source has no ressource fork, */
-+                return AFP_OK;
-+            
-+            /* We are here  because :
-+             * -there's no dest folder. 
-+             * -there's no .AppleDouble in the dest folder.
-+             * if we use the struct adouble passed in parameter it will not
-+             * create .AppleDouble if the file is already opened, so we
-+             * use a diff one, it's not a pb,ie it's not the same file, yet.
-+             */
-+            ad_init(&ad, vol->v_adouble); 
-+            if (!ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) {
-+              ad_close(&ad, ADFLAGS_HF);
-+              if (!unix_rename( adsrc, vol->vfs->ad_path( dst, 0 )) ) 
-+                   err = 0;
-+                else 
-+                   err = errno;
-+            }
-+            else { /* it's something else, bail out */
-+                  err = errno;
-+              }
-+          }
-+      }
-+      if (err) {
-+              errno = err;
-+              return -1;
-+      }
-+      return 0;
-+}
-+
-+struct vfs_ops netatalk_adouble = {
-+    /* ad_path:           */ ad_path,
-+    /* validupath:        */ validupath_adouble,
-+    /* rf_chown:          */ RF_chown_adouble,
-+    /* rf_renamedir:      */ RF_renamedir_adouble,
-+    /* rf_deletecurdir:   */ RF_deletecurdir_adouble,
-+    /* rf_setfilmode:     */ RF_setfilmode_adouble,
-+    /* rf_setdirmode:     */ RF_setdirmode_adouble,
-+    /* rf_setdirunixmode: */ RF_setdirunixmode_adouble,
-+    /* rf_setdirowner:    */ RF_setdirowner_adouble,
-+    /* rf_deletefile:     */ RF_deletefile_adouble,
-+    /* rf_renamefile:     */ RF_renamefile_adouble,
-+};
-+
-+/* =======================================
-+ osx adouble format 
-+ */
-+static int validupath_osx(const struct vol *vol, const char *name) 
-+{
-+    return strncasecmp(name,".Apple", 6) && strncasecmp(name,"._", 2);
-+}
-+
-+/* ---------------- */
-+int RF_renamedir_osx(const struct vol *vol, const char *oldpath, const char *newpath)
-+{
-+    /* We simply move the corresponding ad file as well */
-+    char   tempbuf[258]="._";
-+    return rename(vol->vfs->ad_path(oldpath,0),strcat(tempbuf,newpath));
-+}
-+
-+/* ---------------- */
-+int RF_deletecurdir_osx(const struct vol *vol)
-+{
-+    return netatalk_unlink( vol->vfs->ad_path(".",0) );
-+}
-+
-+/* ---------------- */
-+static int RF_setdirunixmode_osx(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
-+{
-+    return adouble_setfilmode(vol->vfs->ad_path( name, ADFLAGS_DIR ), mode, st);
-+}
-+
-+/* ---------------- */
-+static int RF_setdirmode_osx(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
-+{
-+    return 0;
-+}
-+
-+/* ---------------- */
-+static int RF_setdirowner_osx(const struct vol *vol, const char *path, uid_t uid, gid_t gid)
-+{
-+      return 0;
-+}
-+
-+/* ---------------- */
-+int RF_renamefile_osx(const struct vol *vol, const char *src, const char *dst)
-+{
-+    char  adsrc[ MAXPATHLEN + 1];
-+
-+    strcpy( adsrc, vol->vfs->ad_path( src, 0 ));
-+    return unix_rename( adsrc, vol->vfs->ad_path( dst, 0 ));
-+}
-+
-+struct vfs_ops netatalk_adouble_osx = {
-+    /* ad_path:          */ ad_path_osx,
-+    /* validupath:       */ validupath_osx,
-+    /* rf_chown:         */ RF_chown_adouble,
-+    /* rf_renamedir:     */ RF_renamedir_osx,
-+    /* rf_deletecurdir:  */ RF_deletecurdir_osx,
-+    /* rf_setfilmode:    */ RF_setfilmode_adouble,
-+    /* rf_setdirmode:    */ RF_setdirmode_osx,
-+    /* rf_setdirunixmode:*/ RF_setdirunixmode_osx,
-+    /* rf_setdirowner:   */ RF_setdirowner_osx,
-+    /* rf_deletefile:    */ RF_deletefile_adouble,
-+    /* rf_renamefile:    */ RF_renamefile_osx,
-+};
-+
-+/* =======================================
-+   samba ads format 
-+ */
-+struct vfs_ops netatalk_adouble_ads = {
-+    /* ad_path:          */ ad_path_ads,
-+    /* validupath:       */ validupath_adouble,
-+    /* rf_chown:         */ RF_chown_ads,
-+    /* rf_renamedir:     */ RF_renamedir_adouble,
-+    /* rf_deletecurdir:  */ RF_deletecurdir_ads,
-+    /* rf_setfilmode:    */ RF_setfilmode_ads,
-+    /* rf_setdirmode:    */ RF_setdirmode_ads,
-+    /* rf_setdirunixmode:*/ RF_setdirunixmode_ads,
-+    /* rf_setdirowner:   */ RF_setdirowner_ads,
-+    /* rf_deletefile:    */ RF_deletefile_ads,
-+    /* rf_renamefile:    */ RF_renamefile_ads,
-+};
-+
-+/* ---------------- */
-+void initvol_vfs(struct vol *vol)
-+{
-+    if (vol->v_adouble == AD_VERSION2_OSX) {
-+        vol->vfs = &netatalk_adouble_osx;
-+    }
-+    else if (vol->v_adouble == AD_VERSION1_ADS) {
-+        vol->vfs = &netatalk_adouble_ads;
-+    }
-+    else {
-+        vol->vfs = &netatalk_adouble;
-+    }
-+}
-+
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/volume.c ./etc/afpd/volume.c
---- ../src.dev2/etc/afpd/volume.c      Mon Jul 12 08:46:03 2004
-+++ ./etc/afpd/volume.c        Mon Jul 12 00:29:11 2004
-@@ -427,6 +427,8 @@
-             options[VOLOPT_ADOUBLE].i_value = AD_VERSION2;
-         else if (strcasecmp(val + 1, "osx") == 0)
-             options[VOLOPT_ADOUBLE].i_value = AD_VERSION2_OSX;
-+        else if (strcasecmp(val + 1, "ads") == 0)
-+            options[VOLOPT_ADOUBLE].i_value = AD_VERSION1_ADS;
- #endif
-     } else if (optionok(tmp, "options:", val)) {
-         char *p;
-@@ -523,34 +525,6 @@
-     }
- }
--/* ----------------- 
-- * FIXME should be define elsewhere
--*/
--static int validupath_adouble(const struct vol *vol, const char *name) 
--{
--    return (vol->v_flags & AFPVOL_USEDOTS) ? strncasecmp(name,".Apple", 6) && strcasecmp(name, ".Parent")
--                                           : name[0] != '.';
--}                                           
--
--/* ----------------- */
--static int validupath_osx(const struct vol *vol, const char *name) 
--{
--    return strncasecmp(name,".Apple", 6) && strncasecmp(name,"._", 2);
--}             
--
--/* ---------------- */
--static void initvoladouble(struct vol *vol)
--{
--    if (vol->v_adouble == AD_VERSION2_OSX) {
--        vol->validupath  = validupath_osx;
--        vol->ad_path     = ad_path_osx;
--    }
--    else {
--        vol->validupath  = validupath_adouble;
--        vol->ad_path     = ad_path;
--    }
--}
--
- /* ------------------------------- */
- static int creatvol(AFPObj *obj, struct passwd *pwd, 
-                     char *path, char *name, 
-@@ -653,7 +627,8 @@
-           volume->v_adouble = options[VOLOPT_ADOUBLE].i_value;
-       else 
-           volume->v_adouble = AD_VERSION;
--      initvoladouble(volume);
-+
-+      initvol_vfs(volume);
- #ifdef FORCE_UIDGID
-         if (options[VOLOPT_FORCEUID].c_value) {
-             volume->v_forceuid = strdup(options[VOLOPT_FORCEUID].c_value);
-@@ -2231,6 +2206,9 @@
-             break;
-         case AD_VERSION2_OSX:
-             strlcat(buf, "ADOUBLE_VER:osx\n", sizeof(buf));
-+            break;
-+        case AD_VERSION1_ADS:
-+            strlcat(buf, "ADOUBLE_VER:ads\n", sizeof(buf));
-             break;
-     }
-diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/volume.h ./etc/afpd/volume.h
---- ../src.dev2/etc/afpd/volume.h      Mon Jul 12 08:46:03 2004
-+++ ./etc/afpd/volume.h        Fri Jun 25 22:01:56 2004
-@@ -14,6 +14,7 @@
- #include "atalk/unicode.h"
- #include "globals.h"
-+#include "afp_vfs.h"
- #define AFPVOL_NAMELEN   27
-@@ -75,6 +76,7 @@
-     int                 v_preexec_close;
-     
-     /* adouble indirection */
-+    struct vfs_ops      *vfs;
-     int                 (*validupath)(const struct vol *, const char *);
-     char                *(*ad_path)(const char *, int);
- };
-diff -Nur -X .cvsignore -x CVS ../src.dev2/include/atalk/adouble.h ./include/atalk/adouble.h
---- ../src.dev2/include/atalk/adouble.h        Tue Jun 15 01:08:28 2004
-+++ ./include/atalk/adouble.h  Sun Jun 20 22:33:26 2004
-@@ -82,6 +82,7 @@
- #define AD_VERSION1   0x00010000
- #define AD_VERSION2   0x00020000
- #define AD_VERSION2_OSX       0x00020001
-+#define AD_VERSION1_ADS       0x00010002
- #define AD_VERSION    AD_VERSION2
- /*
-@@ -252,6 +253,7 @@
-                                         the header parameter size is too small.
-                                      */
-     char                *(*ad_path)(const char *, int);
-+    int                 (*ad_mkrf)(char *);
-                            
- #ifdef USE_MMAPPED_HEADERS
-     char                *ad_data;
-@@ -364,6 +366,7 @@
- extern char *ad_dir       __P((const char *));
- extern char *ad_path      __P((const char *, int));
- extern char *ad_path_osx  __P((const char *, int));
-+extern char *ad_path_ads  __P((const char *, int));
- extern int ad_mode        __P((const char *, int));
- extern int ad_mkdir       __P((const char *, int));
-diff -Nur -X .cvsignore -x CVS ../src.dev2/libatalk/adouble/ad_open.c ./libatalk/adouble/ad_open.c
---- ../src.dev2/libatalk/adouble/ad_open.c     Mon Jul 12 02:01:45 2004
-+++ ./libatalk/adouble/ad_open.c       Mon Jul 12 02:12:25 2004
-@@ -697,6 +697,25 @@
-     return( pathbuf );
- }
-+/* -------------------- */
-+static int ad_mkrf(char *path)
-+{
-+    char *slash;
-+    /*
-+     * Probably .AppleDouble doesn't exist, try to mkdir it.
-+     */
-+     if (NULL == ( slash = strrchr( path, '/' )) ) {
-+         return -1;
-+     }
-+     *slash = '\0';
-+     errno = 0;
-+     if ( ad_mkdir( path, 0777 ) < 0 ) {
-+          return -1;
-+     }
-+     *slash = '/';
-+     return 0;
-+}
-+
- /* ---------------------------------------
-  * Put the resource fork where it needs to be:
-  * ._name
-@@ -729,8 +748,97 @@
-     strlcat( pathbuf, slash, MAXPATHLEN +1);
-     return pathbuf;
- }
-+/* -------------------- */
-+static int ad_mkrf_osx(char *path)
-+{
-+    return 0;
-+}
--/*
-+/* ---------------------------------------
-+ * Put the .AppleDouble where it needs to be:
-+ *
-+ *        /   a/.AppleDouble/b/Afp_AfpInfo
-+ *    a/b     
-+ *        \   b/.AppleDouble/.Parent/Afp_AfpInfo
-+ *
-+ */
-+char *
-+ad_path_ads( path, adflags )
-+    const char        *path;
-+    int               adflags;
-+{
-+    static char       pathbuf[ MAXPATHLEN + 1];
-+    char      c, *slash, buf[MAXPATHLEN + 1];
-+    size_t      l;
-+
-+    l = strlcpy(buf, path, MAXPATHLEN +1);
-+    if ( adflags & ADFLAGS_DIR ) {
-+      strcpy( pathbuf, buf);
-+      if ( *buf != '\0' && l < MAXPATHLEN) {
-+          pathbuf[l++] = '/';
-+          pathbuf[l] = 0;
-+      }
-+      slash = ".Parent";
-+    } else {
-+      if (NULL != ( slash = strrchr( buf, '/' )) ) {
-+          c = *++slash;
-+          *slash = '\0';
-+          strcpy( pathbuf, buf);
-+          *slash = c;
-+      } else {
-+          pathbuf[ 0 ] = '\0';
-+          slash = buf;
-+      }
-+    }
-+    strlcat( pathbuf, ".AppleDouble/", MAXPATHLEN +1);
-+    strlcat( pathbuf, slash, MAXPATHLEN +1);
-+
-+    strlcat( pathbuf, "/Afp_AfpInfo", MAXPATHLEN +1);
-+
-+#if 0
-+    if ((adflags & ADFLAGS_HF)) {
-+        strlcat( pathbuf, "Afp_AfpInfo", MAXPATHLEN +1);
-+    else {
-+        strlcat( pathbuf, "Afp_Resource", MAXPATHLEN +1);
-+    }
-+#endif      
-+    return( pathbuf );
-+}
-+
-+/* -------------------- */
-+static int ad_mkrf_ads(char *path)
-+{
-+    char *slash;
-+    /*
-+     * Probably .AppleDouble doesn't exist, try to mkdir it.
-+     */
-+     if (NULL == ( slash = strrchr( path, '/' )) ) {
-+         return -1;
-+     }
-+     *slash = 0;
-+     errno = 0;
-+     if ( ad_mkdir( path, 0777 ) < 0 ) {
-+         if ( errno == ENOENT ) {
-+             char *slash1;
-+             
-+             if (NULL == ( slash1 = strrchr( path, '/' )) ) 
-+                 return -1;
-+             errno = 0;
-+             *slash1 = 0;
-+             if ( ad_mkdir( path, 0777 ) < 0 ) 
-+                  return -1;
-+             *slash1 = '/';
-+             if ( ad_mkdir( path, 0777 ) < 0 )
-+                 return -1;
-+         }
-+         else
-+            return -1;
-+     }     
-+     *slash = '/';
-+     return 0;
-+}
-+
-+/* -------------------------
-  * Support inherited protection modes for AppleDouble files.  The supplied
-  * mode is ANDed with the parent directory's mask value in lieu of "umask",
-  * and that value is returned.
-@@ -914,10 +1022,16 @@
-     memset( ad, 0, sizeof( struct adouble ) );
-     ad->ad_flags = flags;
-     if (flags == AD_VERSION2_OSX) {
--        ad->ad_path     = ad_path_osx;
-+        ad->ad_path = ad_path_osx;
-+        ad->ad_mkrf = ad_mkrf_osx;
-+    }
-+    else if (flags == AD_VERSION1_ADS) {
-+        ad->ad_path = ad_path_ads;
-+        ad->ad_mkrf = ad_mkrf_ads;
-     }
-     else {
--        ad->ad_path     = ad_path;
-+        ad->ad_path = ad_path;
-+        ad->ad_mkrf = ad_mkrf;
-     }
- }
-@@ -931,7 +1045,7 @@
-     struct adouble    *ad;
- {
-     struct stat         st;
--    char              *slash, *ad_p;
-+    char              *ad_p;
-     int                       hoflags, admode;
-     int                 st_invalid;
-     int                 open_df = 0;
-@@ -1031,19 +1145,9 @@
-           st_invalid = ad_mode_st(ad_p, &admode, &st);
-           admode = ad_hf_mode(admode); 
-           if ( errno == ENOENT && !(adflags & ADFLAGS_NOADOUBLE) && ad->ad_flags != AD_VERSION2_OSX) {
--              /*
--               * Probably .AppleDouble doesn't exist, try to
--               * mkdir it.
--               */
--              if (NULL == ( slash = strrchr( ad_p, '/' )) ) {
--                  return ad_error(ad, adflags);
--              }
--              *slash = '\0';
--              errno = 0;
--              if ( ad_mkdir( ad_p, 0777 ) < 0 ) {
-+              if (ad->ad_mkrf( ad_p) < 0) {
-                   return ad_error(ad, adflags);
--              }
--              *slash = '/';
-+              }
-               admode = mode;
-               st_invalid = ad_mode_st(ad_p, &admode, &st);
-               admode = ad_hf_mode(admode); 
diff --git a/contrib/patches/patch.mangled_trash_with_ip b/contrib/patches/patch.mangled_trash_with_ip
deleted file mode 100644 (file)
index 38976fc..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-Workaround for Network Trash and system without byte locking (broken nfs/afs)
-mangle OS9 "Network Trash Folder/Trash Can #2" name to
-"Network Trash Folder/Trash Can #2.<client ip>.<tcp port>"
-So multiple clients can share the same volume and have a working trash.
-
-Index: etc/afpd/directory.c
-===================================================================
-RCS file: /cvsroot/netatalk/netatalk/etc/afpd/directory.c,v
-retrieving revision 1.71.2.4.2.12
-diff -u -r1.71.2.4.2.12 directory.c
---- etc/afpd/directory.c       11 Mar 2004 16:16:40 -0000      1.71.2.4.2.12
-+++ etc/afpd/directory.c       21 Apr 2004 12:42:03 -0000
-@@ -554,6 +554,7 @@
-  * attempt to extend the current dir. tree to include path
-  * as a side-effect, movecwd to that point and return the new dir
-  */
-+
- static struct dir *
-             extenddir( vol, dir, path )
- struct vol    *vol;
-@@ -563,7 +564,25 @@
-     char *save_m_name;
-     if ( path->u_name == NULL) {
--        path->u_name = mtoupath(vol, path->m_name, dir->d_did, (path->m_type==3) );
-+#ifdef DISABLE_LOCKING
-+        int l = strlen(TRASH_PREFIX);
-+        /* XXX replace mac name with unix name */
-+      if (vol->v_trash_id && vol->v_trash_id  == dir->d_did && vol->v_ip &&
-+             !strncmp(TRASH_PREFIX , path->m_name, l ) )
-+      {
-+          static char temp[MAXPATHLEN + 1];
-+          char *u;
-+
-+          strcpy(temp, path->m_name);
-+          u = temp +l;
-+          strcat(temp, ".");
-+          strcat(temp, vol->v_ip);
-+          path->u_name = temp;
-+      
-+      }
-+        else 
-+#endif
-+            path->u_name = mtoupath(vol, path->m_name, dir->d_did, (path->m_type==3) );
-     }
-     path->dir = NULL;
-Index: etc/afpd/enumerate.c
-===================================================================
-RCS file: /cvsroot/netatalk/netatalk/etc/afpd/enumerate.c,v
-retrieving revision 1.39.2.2.2.4
-diff -u -r1.39.2.2.2.4 enumerate.c
---- etc/afpd/enumerate.c       11 Mar 2004 02:01:59 -0000      1.39.2.2.2.4
-+++ etc/afpd/enumerate.c       21 Apr 2004 12:42:04 -0000
-@@ -54,9 +54,39 @@
-     if (id == 0) {
-         return NULL;
-     }
-+    
-+#ifdef DISABLE_LOCKING
-+    if (!path->m_name) {
-+        int l = strlen(TRASH_PREFIX);
-+        /* XXX */
-+      if (vol->v_trash_id && vol->v_trash_id  == dir->d_did && vol->v_ip &&
-+             !strncmp(TRASH_PREFIX , upath, l ) )
-+      {
-+          static char temp[MAXPATHLEN + 1];
-+          char *u;
-+
-+          strcpy(temp, upath);
-+          u = temp +l;
-+          
-+          while (*u >= '0' && *u <= '9') {
-+              u++;
-+          }
-+          if (*u == '.') {
-+              *u = '\0';
-+          }
-+          path->m_name = temp; 
-+      }
-+          
-+        else if(!(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
-+           return NULL;
-+        }
-+    }
-+#else 
-     if (!path->m_name && !(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
--        return NULL;
-+       return NULL;
-     }
-+#endif
-+    
-     name  = path->m_name;    
-     if ((cdir = dirnew(name, upath)) == NULL) {
-         LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
-@@ -185,6 +215,32 @@
-     return name;
- }
-+#ifdef DISABLE_LOCKING
-+/* ----------------------------- */
-+int check_trash(const struct vol *vol, char *name)
-+{
-+int l = strlen(TRASH_PREFIX);
-+char *u;
-+
-+    if (strncmp(TRASH_PREFIX , name, l)) 
-+        return 0;
-+    /* */
-+    u = name +l;
-+    while (*u >= '0' && *u <= '9') {
-+        u++;
-+    }
-+
-+    if (u == name +l)
-+        return 0;
-+
-+    if (*u == '.' && !strcmp(vol->v_ip, u +1)) {
-+        return 0;
-+    }
-+    /* hide this one */
-+    return 1;
-+}
-+#endif
-+
- /* ----------------------------- */
- int 
- for_each_dirent(const struct vol *vol, char *name, dir_loop fn, void *data)
-@@ -193,15 +249,28 @@
-     struct dirent     *de;
-     char            *m_name;
-     int             ret;
-+#ifdef DISABLE_LOCKING
-+    int             mangle_trash = 0;
-+#endif
-     
-     if (NULL == ( dp = opendir( name)) ) {
-         return -1;
-     }
-+
-+#ifdef DISABLE_LOCKING
-+    if (vol->v_trash_id && vol->v_trash_id == curdir->d_did && !strcmp(name, ".")) {
-+        mangle_trash = 1;
-+    }
-+#endif    
-     ret = 0;
-     for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
-         if (!(m_name = check_dirent(vol, de->d_name)))
-             continue;
-+#ifdef DISABLE_LOCKING
-+        if (mangle_trash && check_trash(vol, de->d_name))
-+            continue;
-+#endif
-         ret++;
-         if (fn && fn(de,m_name, data) < 0) {
-            closedir(dp);
-Index: etc/afpd/volume.c
-===================================================================
-RCS file: /cvsroot/netatalk/netatalk/etc/afpd/volume.c,v
-retrieving revision 1.51.2.7.2.28
-diff -u -r1.51.2.7.2.28 volume.c
---- etc/afpd/volume.c  6 Apr 2004 23:29:37 -0000       1.51.2.7.2.28
-+++ etc/afpd/volume.c  21 Apr 2004 12:42:05 -0000
-@@ -73,7 +73,11 @@
- static struct vol *Volumes = NULL;
- static u_int16_t      lastvid = 0;
--static char           *Trash = "\02\024Network Trash Folder";
-+
-+/* type, len, name */
-+static char           *Trash2 = "\02\024Network Trash Folder";
-+/* type, hint (4 bytes), len (2bytes), name */
-+static char           *Trash3 = "\03\0\0\0\0\0\024Network Trash Folder";
- static struct extmap  *Extmap = NULL, *Defextmap = NULL;
- static int              Extmap_cnt;
-@@ -1038,6 +1042,10 @@
-     free(vol->v_forceuid);
-     free(vol->v_forcegid);
- #endif /* FORCE_UIDGID */
-+
-+#ifdef DISABLE_LOCKING
-+    free(vol->v_ip);
-+#endif    
- }
- /* ------------------------------- */
-@@ -1730,9 +1738,31 @@
-               goto openvol_err;
-           }
-       }
--      else {
--            p = Trash;
--            cname( volume, volume->v_dir, &p );
-+#ifndef DISABLE_LOCKING       
-+      else 
-+#endif        
-+        {
-+            struct path *s_path;
-+
-+            /* use the right name format */
-+            p = (afp_version>= 30)?Trash3:Trash2;
-+            s_path = cname( volume, volume->v_dir, &p );
-+#ifdef DISABLE_LOCKING
-+            if (s_path && *s_path->m_name == '\0' ) {
-+                /* XXXX should do the same with ASP, could use volxlate but there's ':' in $p */
-+                if (obj->proto == AFPPROTO_DSI) {
-+                    DSI *dsi = obj->handle;
-+                
-+                    /* cname moved into dest folder */
-+                    volume->v_trash_id = curdir->d_did;
-+                    volume->v_ip = malloc(MAXPATHLEN +1);
-+                    if (volume->v_ip) {
-+                        sprintf(volume->v_ip, "%s.%u", inet_ntoa(dsi->client.sin_addr),
-+                              ntohs(dsi->client.sin_port));
-+                    }
-+                }
-+            }
-+#endif
-         }
-         return( AFP_OK );
-     }
-Index: etc/afpd/volume.h
-===================================================================
-RCS file: /cvsroot/netatalk/netatalk/etc/afpd/volume.h,v
-retrieving revision 1.19.2.5.2.6
-diff -u -r1.19.2.5.2.6 volume.h
---- etc/afpd/volume.h  11 Mar 2004 02:02:04 -0000      1.19.2.5.2.6
-+++ etc/afpd/volume.h  21 Apr 2004 12:42:05 -0000
-@@ -81,6 +81,12 @@
-     /* adouble indirection */
-     int                 (*validupath)(const struct vol *, const char *);
-     char                *(*ad_path)(const char *, int);
-+
-+#ifdef DISABLE_LOCKING
-+    /* for OS 9 trash when there's no working byte locking (afs, nfs) */
-+    cnid_t            v_trash_id;
-+    char              *v_ip;
-+#endif    
- };
- #ifdef NO_LARGE_VOL_SUPPORT
-@@ -167,6 +173,8 @@
- #define VOLPBIT_BSIZE   11        /* block size */
-+#define TRASH_PREFIX "Trash Can #"
-+
- #define vol_noadouble(vol) (((vol)->v_flags & AFPVOL_NOADOUBLE) ? \
-                           ADFLAGS_NOADOUBLE : 0)
- #ifdef AFP3x
diff --git a/contrib/patches/patch.samba.3.0.5pre2-SVN b/contrib/patches/patch.samba.3.0.5pre2-SVN
deleted file mode 100644 (file)
index 8b17608..0000000
+++ /dev/null
@@ -1,453 +0,0 @@
-Index: source/smbd/nttrans.c
-===================================================================
---- source/smbd/nttrans.c      (revision 1473)
-+++ source/smbd/nttrans.c      (working copy)
-@@ -661,11 +661,16 @@
-                        * Check to see if this is a mac fork of some kind.
-                        */
--                      if( strchr_m(fname, ':')) {
--                              END_PROFILE(SMBntcreateX);
--                              return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
--                      }
--
-+                        if( !strchr_m(fname, ':')) {
-+                                /* it's not an alternate stream */
-+                                END_PROFILE(SMBntcreateX);
-+                                return(ERROR_DOS(ERRDOS,ERRbadfid));
-+                        }
-+                        else if (-1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0)) {
-+                                /* fs have no support for alternate streams */
-+                                END_PROFILE(SMBntcreateX);
-+                                return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
-+                        }
-                       /*
-                         we need to handle the case when we get a
-                         relative open relative to a file and the
-@@ -673,26 +678,29 @@
-                         (hint from demyn plantenberg)
-                       */
--                      END_PROFILE(SMBntcreateX);
--                      return(ERROR_DOS(ERRDOS,ERRbadfid));
-+                      /*
-+                       * Copy in the base name.
-+                       */
-+                      pstrcpy( fname, dir_fsp->fsp_name );
-+                      dir_name_len = strlen(fname);
-               }
-+              else { /* it's a dir */
-+                      /*
-+                       * Copy in the base directory name.
-+                       */
--              /*
--               * Copy in the base directory name.
--               */
-+                      pstrcpy( fname, dir_fsp->fsp_name );
-+                      dir_name_len = strlen(fname);
--              pstrcpy( fname, dir_fsp->fsp_name );
--              dir_name_len = strlen(fname);
-+                      /*
-+                       * Ensure it ends in a '\'.
-+                       */
--              /*
--               * Ensure it ends in a '\'.
--               */
--
--              if(fname[dir_name_len-1] != '\\' && fname[dir_name_len-1] != '/') {
--                      pstrcat(fname, "/");
--                      dir_name_len++;
--              }
--
-+                      if(fname[dir_name_len-1] != '\\' && fname[dir_name_len-1] != '/') {
-+                              pstrcat(fname, "/");
-+                              dir_name_len++;
-+                      }
-+                }
-               srvstr_get_path(inbuf, rel_fname, smb_buf(inbuf), sizeof(rel_fname), 0, STR_TERMINATE, &status,False);
-               if (!NT_STATUS_IS_OK(status)) {
-                       END_PROFILE(SMBntcreateX);
-@@ -709,7 +717,6 @@
-               /* 
-                * Check to see if this is a mac fork of some kind.
-                */
--
-               if( strchr_m(fname, ':')) {
-                       
- #ifdef HAVE_SYS_QUOTAS
-@@ -725,8 +732,11 @@
-                                */
-                       } else {
- #endif
--                              END_PROFILE(SMBntcreateX);
--                              return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
-+                              if (-1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0)) {  
-+                                      END_PROFILE(SMBntcreateX);
-+                                      return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
-+                              }
-+
- #ifdef HAVE_SYS_QUOTAS
-                       }
- #endif
-@@ -1235,12 +1245,10 @@
-                       }
-                       /*
--                       * Check to see if this is a mac fork of some kind.
-+                       * Check to see if this is a mac fork of some kind. FIXME
-                        */
--
--                      if( strchr_m(fname, ':'))
-+                      if( strchr_m(fname, ':') && -1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0))
-                               return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
--
-                       return ERROR_DOS(ERRDOS,ERRbadfid);
-               }
-@@ -1278,7 +1286,7 @@
-                * Check to see if this is a mac fork of some kind.
-                */
--              if( strchr_m(fname, ':'))
-+              if( strchr_m(fname, ':') && -1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0))
-                       return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
-       }
-Index: source/smbd/vfs.c
-===================================================================
---- source/smbd/vfs.c  (revision 1473)
-+++ source/smbd/vfs.c  (working copy)
-@@ -142,7 +142,10 @@
-               vfswrap_fremovexattr,
-               vfswrap_setxattr,
-               vfswrap_lsetxattr,
--              vfswrap_fsetxattr
-+              vfswrap_fsetxattr,
-+
-+              /* alternate streams operations. */
-+              vfswrap_listads
-       }
- };
-Index: source/smbd/vfs-wrap.c
-===================================================================
---- source/smbd/vfs-wrap.c     (revision 1473)
-+++ source/smbd/vfs-wrap.c     (working copy)
-@@ -1029,3 +1029,14 @@
- {
-       return sys_fsetxattr(fd, name, value, size, flags);
- }
-+
-+/****************************************************************
-+ Alternate stream operations.
-+*****************************************************************/
-+
-+ssize_t vfswrap_listads(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size)
-+{
-+        errno = ENOSYS;
-+        return -1;
-+}
-+
-Index: source/smbd/trans2.c
-===================================================================
---- source/smbd/trans2.c       (revision 1473)
-+++ source/smbd/trans2.c       (working copy)
-@@ -406,6 +406,159 @@
- }
- /****************************************************************************
-+ ****************************************************************************
-+ Return a linked list of the alternate streams Plus the total size
-+****************************************************************************/
-+struct ads_list {
-+      struct ads_list *next, *prev;
-+      struct ads_struct ads;
-+};
-+
-+static struct ads_list *get_ads_list(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp, const char *fname, size_t *pads_total_len)
-+{
-+      /* Get a list of all ads with size, lax namesize is 64k. */
-+      size_t ads_namelist_size = 4096;
-+      char *ads_namelist;
-+      char *p;
-+      ssize_t sizeret;
-+      int i;
-+      struct ads_list *ads_list_head = NULL;
-+
-+      *pads_total_len = 0;
-+
-+      DEBUG(10,("get_ads_list\n" ));
-+      
-+      for (i = 0, ads_namelist = talloc(mem_ctx, ads_namelist_size); i < 6;
-+                      ads_namelist = talloc_realloc(mem_ctx, ads_namelist, ads_namelist_size), i++) {
-+
-+              sizeret = SMB_VFS_LISTADS(conn, fname, ads_namelist, ads_namelist_size);
-+              if (sizeret == -1 && errno == ERANGE) {
-+                      ads_namelist_size *= 2;
-+              } else {
-+                      break;
-+              }
-+      }
-+
-+      if (sizeret == -1)
-+              return NULL;
-+
-+      DEBUG(10,("get_ads_list: ads_namelist size = %d\n", sizeret ));
-+
-+      if (sizeret) { 
-+              for (p = ads_namelist; p - ads_namelist < sizeret; p += strlen(p) +1) {
-+                      struct ads_list *listp, *tmp;
-+                      SMB_STRUCT_STAT sbuf;
-+                      char *t;
-+                      
-+                      listp = talloc(mem_ctx, sizeof(struct ads_list));
-+                      if (!listp)
-+                              return NULL;
-+
-+                      listp->ads.name = talloc_strdup(mem_ctx, p);
-+                      if (!listp->ads.name)
-+                              return NULL;
-+                      
-+                      listp->ads.size = 0;
-+                      listp->ads.allocation_size = 0;
-+
-+                      t = talloc_asprintf(mem_ctx, "%s%s", fname, p);
-+                      if (!t)
-+                              return NULL;
-+                      if (!SMB_VFS_STAT(conn, t ,&sbuf)) {
-+                              listp->ads.size = get_file_size(sbuf);
-+                              listp->ads.allocation_size = get_allocation_size(NULL,&sbuf);
-+                      }
-+                      /* FIXME get ride of this */
-+                      {
-+                      fstring dos_ads_name;
-+                              
-+                              push_ascii_fstring(dos_ads_name, listp->ads.name);
-+                              *pads_total_len += strlen(dos_ads_name) + 1 + 24;
-+                              DEBUG(10,("get_ads_list: total_len = %u, %s, size = %llu\n",
-+                                              *pads_total_len, dos_ads_name, listp->ads.size ));
-+                      }
-+                      DLIST_ADD_END(ads_list_head, listp, tmp);
-+              }
-+      }
-+
-+      DEBUG(10,("get_ads_list: total_len = %u\n", *pads_total_len));
-+      return ads_list_head;
-+}
-+
-+/****************************************************************************
-+ Fill a qfilepathinfo buffer with alternate streams. 
-+ Returns the length of the buffer that was filled.
-+****************************************************************************/
-+
-+static unsigned int fill_ads_buffer(char *pdata, unsigned int total_data_size,
-+      connection_struct *conn, files_struct *fsp, const char *fname)
-+{
-+      unsigned int ret_data_size = 0;
-+      char *p = pdata;
-+      size_t total_ads_len;
-+      TALLOC_CTX *mem_ctx;
-+      struct ads_list *ads_list;
-+
-+      SMB_ASSERT(total_data_size >= 24);
-+
-+      mem_ctx = talloc_init("fill_ads_buffer");
-+      if (!mem_ctx) {
-+              return 0;
-+      }
-+
-+      ads_list = get_ads_list(mem_ctx, conn, fsp, fname, &total_ads_len);
-+      if (!ads_list) {
-+              talloc_destroy(mem_ctx);
-+              return 0;
-+      }
-+
-+      if (total_ads_len > total_data_size) {
-+              talloc_destroy(mem_ctx);
-+              return 0;
-+      }
-+
-+      for (p = pdata; ads_list; ads_list = ads_list->next) {
-+#if 0
-+              size_t dos_namelen;
-+              fstring dos_ads_name;
-+
-+              push_ascii_fstring(dos_ads_name, ads_list->ads.name);
-+              dos_namelen = strlen(dos_ads_name);
-+              if (dos_namelen > 255 || dos_namelen == 0) {
-+                      break;
-+              }
-+              if (dos_namelen + 24 > total_data_size) {
-+                      break;
-+              }
-+#endif
-+              /* We know we have room. */
-+              size_t byte_len = dos_PutUniCode(p +24, ads_list->ads.name, -1, False);
-+              size_t off = SMB_ROUNDUP(24 +byte_len, 8); 
-+              
-+              SIVAL(p,0,0); /* from ethereal next entry offset */
-+              SIVAL(p,4, byte_len); /* Byte length of unicode string :filename:$DATA */
-+              SOFF_T(p,8, ads_list->ads.size);
-+              SOFF_T(p,16, ads_list->ads.allocation_size);
-+              if (ads_list->next) {
-+                  SIVAL(p,0, off);
-+              }
-+              else {
-+                  /* don't pad the last one */
-+                  off = 24 +byte_len;
-+              }
-+
-+              total_data_size -= off;
-+              p += off;
-+      }
-+
-+      ret_data_size = PTR_DIFF(p, pdata);
-+      DEBUG(10,("fill_ads_buffer: data_size = %u, total_ads_len = %u\n",
-+                      ret_data_size, total_ads_len ));
-+      talloc_destroy(mem_ctx);
-+      return ret_data_size;
-+}
-+
-+/****************************************************************************
-   Send the required number of replies back.
-   We assume all fields other than the data fields are
-   set correctly for the type of call.
-@@ -2653,7 +2806,7 @@
-                       data_size = 4;
-                       break;
--#if 0
-+#if 1
-               /*
-                * NT4 server just returns "invalid query" to this - if we try to answer
-                * it then NTws gets a BSOD! (tridge).
-@@ -2663,16 +2816,24 @@
- #endif
-               case SMB_FILE_STREAM_INFORMATION:
-                       DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_STREAM_INFORMATION\n"));
--                      if (mode & aDIR) {
--                              data_size = 0;
--                      } else {
--                              size_t byte_len = dos_PutUniCode(pdata+24,"::$DATA", 0xE, False);
--                              SIVAL(pdata,0,0); /* ??? */
--                              SIVAL(pdata,4,byte_len); /* Byte length of unicode string ::$DATA */
--                              SOFF_T(pdata,8,file_size);
--                              SIVAL(pdata,16,allocation_size);
--                              SIVAL(pdata,20,0); /* ??? */
--                              data_size = 24 + byte_len;
-+                      {
-+                              size_t off;
-+ 
-+                              if (mode & aDIR) {
-+                                      off = 0;
-+                              } else {
-+                                      size_t byte_len = dos_PutUniCode(pdata+24,"::$DATA", 0xE, False);
-+                              
-+                                      off = SMB_ROUNDUP(24 +byte_len, 8); /* FIXME or 8 ? */
-+                                      SIVAL(pdata,0,0); /* from ethereal next entry offset */
-+                                      SIVAL(pdata,4,byte_len); /* Byte length of unicode string ::$DATA */
-+                                      SOFF_T(pdata,8,file_size);
-+                                      SOFF_T(pdata,16,allocation_size);
-+                              }
-+                              if ((data_size = fill_ads_buffer(pdata +off, data_size, conn, fsp, fname))) {
-+                                      SIVAL(pdata,0,off);
-+                              }
-+                              data_size += off;
-                       }
-                       break;
-Index: source/include/vfs_macros.h
-===================================================================
---- source/include/vfs_macros.h        (revision 1473)
-+++ source/include/vfs_macros.h        (working copy)
-@@ -119,6 +119,9 @@
- #define SMB_VFS_LSETXATTR(conn,path,name,value,size,flags) ((conn)->vfs.ops.lsetxattr((conn)->vfs.handles.lsetxattr,(conn),(path),(name),(value),(size),(flags)))
- #define SMB_VFS_FSETXATTR(fsp,fd,name,value,size,flags) ((fsp)->conn->vfs.ops.fsetxattr((fsp)->conn->vfs.handles.fsetxattr,(fsp),(fd),(name),(value),(size),(flags)))
-+/* ADS operations. */
-+#define SMB_VFS_LISTADS(conn,path,list,size) ((conn)->vfs.ops.listads((conn)->vfs.handles.listads,(conn),(path),(list),(size)))
-+
- /*******************************************************************
-  Don't access conn->vfs_opaque.ops directly!!!
-  Use this macros!
-@@ -217,6 +220,9 @@
- #define SMB_VFS_OPAQUE_LSETXATTR(conn,path,name,value,size,flags) ((conn)->vfs_opaque.ops.lsetxattr((conn)->vfs_opaque.handles.lsetxattr,(conn),(path),(name),(value),(size),(flags)))
- #define SMB_VFS_OPAQUE_FSETXATTR(fsp,fd,name,value,size,flags) ((fsp)->conn->vfs_opaque.ops.fsetxattr((fsp)->conn->vfs_opaque.handles.fsetxattr,(fsp),(fd),(name),(value),(size),(flags)))
-+/* ADS operations. */
-+#define SMB_VFS_OPAQUE_LISTADS(conn,path,list,size) ((conn)->vfs_opaque.ops.listads((conn)->vfs_opaque.handles.listads,(conn),(path),(list),(size)))
-+
- /*******************************************************************
-  Don't access handle->vfs_next.ops.* directly!!!
-  Use this macros!
-@@ -315,4 +321,7 @@
- #define SMB_VFS_NEXT_LSETXATTR(handle,conn,path,name,value,size,flags) ((handle)->vfs_next.ops.lsetxattr((handle)->vfs_next.handles.lsetxattr,(conn),(path),(name),(value),(size),(flags)))
- #define SMB_VFS_NEXT_FSETXATTR(handle,fsp,fd,name,value,size,flags) ((handle)->vfs_next.ops.fsetxattr((handle)->vfs_next.handles.fsetxattr,(fsp),(fd),(name),(value),(size),(flags)))
-+/* ADS operations. */
-+#define SMB_VFS_NEXT_LISTADS(handle,conn,path,list,size) ((handle)->vfs_next.ops.listads((handle)->vfs_next.handles.listads,(conn),(path),(list),(size)))
-+
- #endif /* _VFS_MACROS_H */
-Index: source/include/vfs.h
-===================================================================
---- source/include/vfs.h       (revision 1473)
-+++ source/include/vfs.h       (working copy)
-@@ -55,7 +55,8 @@
- /* Changed to version 8 includes EA calls. JRA. */
- /* Changed to version 9 to include the get_shadow_data call. --metze */
- /* Changed to version 10 to include pread/pwrite calls. */
--#define SMB_VFS_INTERFACE_VERSION 10
-+/* Changed to version 11 to include alternate data streams. */
-+#define SMB_VFS_INTERFACE_VERSION 11
- /* to bug old modules witch are trying to compile with the old functions */
-@@ -185,6 +186,9 @@
-       SMB_VFS_OP_SETXATTR,
-       SMB_VFS_OP_LSETXATTR,
-       SMB_VFS_OP_FSETXATTR,
-+      
-+      /* alternate stream */
-+      SMB_VFS_OP_LISTADS,
-       /* This should always be last enum value */
-       
-@@ -294,6 +298,9 @@
-               int (*lsetxattr)(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags);
-               int (*fsetxattr)(struct vfs_handle_struct *handle, struct files_struct *fsp,int filedes, const char *name, const void *value, size_t size, int flags);
-+              /* alternate stream operations. */
-+              ssize_t (*listads)(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size);
-+
-       } ops;
-       struct vfs_handles_pointers {
-@@ -394,6 +401,8 @@
-               struct vfs_handle_struct *lsetxattr;
-               struct vfs_handle_struct *fsetxattr;
-+              /* alternate stream operations. */
-+              struct vfs_handle_struct *listads;
-       } handles;
- };
-Index: source/include/smb.h
-===================================================================
---- source/include/smb.h       (revision 1473)
-+++ source/include/smb.h       (working copy)
-@@ -1703,6 +1703,12 @@
-       DATA_BLOB value;
- };
-+struct ads_struct {
-+      SMB_BIG_UINT size;
-+      SMB_BIG_UINT allocation_size;
-+      char *name;
-+};
-+
- /* EA names used internally in Samba. KEEP UP TO DATE with prohibited_ea_names in trans2.c !. */
- #define SAMBA_POSIX_INHERITANCE_EA_NAME "user.SAMBA_PAI"
- /* EA to use for DOS attributes */
diff --git a/contrib/patches/patch.samba.3.0a20 b/contrib/patches/patch.samba.3.0a20
deleted file mode 100644 (file)
index 6d84621..0000000
+++ /dev/null
@@ -1,407 +0,0 @@
-diff -ur ../smb3.0a20.orig/source/include/smb.h ./source/include/smb.h
---- ../smb3.0a20.orig/source/include/smb.h     Mon Jan  6 18:04:22 2003
-+++ ./source/include/smb.h     Fri Jun  4 05:34:14 2004
-@@ -1652,4 +1652,10 @@
- extern struct poptOption popt_common_debug[];
-+struct ads_struct {
-+      SMB_BIG_UINT size;
-+      SMB_BIG_UINT allocation_size;
-+      char *name;
-+};
-+
- #endif /* _SMB_H */
-diff -ur ../smb3.0a20.orig/source/include/smb_macros.h ./source/include/smb_macros.h
---- ../smb3.0a20.orig/source/include/smb_macros.h      Mon Jan  6 18:04:22 2003
-+++ ./source/include/smb_macros.h      Fri Jun  4 18:24:20 2004
-@@ -291,4 +291,8 @@
- #define vfs_chdir(conn,fname) ((conn)->vfs_ops.chdir((conn),fname))
-+/*******************************************************************
-+ A wrapper for vfs_listads().
-+********************************************************************/
-+#define vfs_listads(conn,path,list,size)  ((conn)->vfs_ops.listads((conn),(path),(list),(size))) 
- #endif /* _SMB_MACROS_H */
-diff -ur ../smb3.0a20.orig/source/include/vfs.h ./source/include/vfs.h
---- ../smb3.0a20.orig/source/include/vfs.h     Mon Jan  6 18:04:23 2003
-+++ ./source/include/vfs.h     Fri Jun  4 16:30:14 2004
-@@ -45,7 +45,8 @@
- /* Changed to version 3 for POSIX acl extensions. JRA. */
- /* Changed to version 4 for cascaded VFS interface. Alexander Bokovoy. */
- /* Changed to version 5 for sendfile addition. JRA. */
--#define SMB_VFS_INTERFACE_VERSION 5
-+/* Changed to version 11 to include alternate data streams. */
-+#define SMB_VFS_INTERFACE_VERSION 11
- /* Version of supported cascaded interface backward copmatibility.
-@@ -173,6 +174,9 @@
-       int (*sys_acl_free_text)(struct connection_struct *conn, char *text);
-       int (*sys_acl_free_acl)(struct connection_struct *conn, SMB_ACL_T posix_acl);
-       int (*sys_acl_free_qualifier)(struct connection_struct *conn, void *qualifier, SMB_ACL_TAG_T tagtype);
-+
-+      /* alternate stream operations. */
-+      ssize_t (*listads)(/* struct vfs_handle_struct *handle, */ struct connection_struct *conn,const char *path, char *list, size_t size);
- };
- struct vfs_options {
-@@ -269,6 +273,9 @@
-       SMB_VFS_OP_SYS_ACL_FREE_ACL,
-       SMB_VFS_OP_SYS_ACL_FREE_QUALIFIER,
-       
-+      /* alternate stream */
-+      SMB_VFS_OP_LISTADS,
-+
-       /* This should always be last enum value */
-       
-       SMB_VFS_OP_LAST
-diff -ur ../smb3.0a20.orig/source/smbd/nttrans.c ./source/smbd/nttrans.c
---- ../smb3.0a20.orig/source/smbd/nttrans.c    Mon Jan  6 18:05:44 2003
-+++ ./source/smbd/nttrans.c    Fri Jul  2 07:38:38 2004
-@@ -52,6 +52,8 @@
-       FILE_GENERIC_ALL
- };
-+#define SMB_VFS_LISTADS vfs_listads
-+
- /****************************************************************************
-  Send the required number of replies back.
-  We assume all fields other than the data fields are
-@@ -625,30 +627,40 @@
-                        * Check to see if this is a mac fork of some kind.
-                        */
--                      if( strchr_m(fname, ':')) {
-+                      if( !strchr_m(fname, ':')) {
-+                              /* it's not an alternate stream */
-+                              END_PROFILE(SMBntcreateX);
-+                              return(ERROR_DOS(ERRDOS,ERRbadfid));
-+                      }
-+                      else if (-1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0)) {
-+                              /* fs have no support for alternate streams */
-                               END_PROFILE(SMBntcreateX);
-                               return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
-                       }
--                      END_PROFILE(SMBntcreateX);
--                      return(ERROR_DOS(ERRDOS,ERRbadfid));
--              }
--              /*
--               * Copy in the base directory name.
--               */
-+                      /*
-+                      * Copy in the base name.
-+                      */
-+                      pstrcpy( fname, dir_fsp->fsp_name );
-+                      dir_name_len = strlen(fname);
-+              }
-+              else { /* it's a dir */
-+                      /*
-+                       * Copy in the base directory name.
-+                       */
--              pstrcpy( fname, dir_fsp->fsp_name );
--              dir_name_len = strlen(fname);
-+                      pstrcpy( fname, dir_fsp->fsp_name );
-+                      dir_name_len = strlen(fname);
--              /*
--               * Ensure it ends in a '\'.
--               */
--
--              if(fname[dir_name_len-1] != '\\' && fname[dir_name_len-1] != '/') {
--                      pstrcat(fname, "\\");
--                      dir_name_len++;
--              }
-+                      /*
-+                       * Ensure it ends in a '\'.
-+                       */
-+                      if(fname[dir_name_len-1] != '\\' && fname[dir_name_len-1] != '/') {
-+                              pstrcat(fname, "\\");
-+                              dir_name_len++;
-+                      }
-+                }
-               srvstr_pull_buf(inbuf, &fname[dir_name_len], smb_buf(inbuf), sizeof(fname)-dir_name_len, STR_TERMINATE);
-       } else {
-               srvstr_pull_buf(inbuf, fname, smb_buf(inbuf), sizeof(fname), STR_TERMINATE);
-@@ -656,8 +668,7 @@
-               /* 
-                * Check to see if this is a mac fork of some kind.
-                */
--
--              if( strchr_m(fname, ':')) {
-+              if( strchr_m(fname, ':') && -1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0)) {
-                       END_PROFILE(SMBntcreateX);
-                       return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
-               }
-@@ -1138,12 +1149,10 @@
-                       srvstr_pull(inbuf, fname, params+53, sizeof(fname), total_parameter_count-53, STR_TERMINATE);
-                       /*
--                       * Check to see if this is a mac fork of some kind.
-+                       * Check to see if this is a mac fork of some kind. FIXME ADS
-                        */
--
--                      if( strchr_m(fname, ':'))
-+                      if( strchr_m(fname, ':') && -1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0))
-                               return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
--
-                       return ERROR_DOS(ERRDOS,ERRbadfid);
-               }
-@@ -1172,7 +1181,7 @@
-                * Check to see if this is a mac fork of some kind.
-                */
--              if( strchr_m(fname, ':'))
-+              if( strchr_m(fname, ':')  && -1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0))
-                       return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
-       }
-diff -ur ../smb3.0a20.orig/source/smbd/trans2.c ./source/smbd/trans2.c
---- ../smb3.0a20.orig/source/smbd/trans2.c     Mon Jan  6 18:05:48 2003
-+++ ./source/smbd/trans2.c     Thu Jul  1 03:06:42 2004
-@@ -50,6 +50,162 @@
-       return ret;
- }
-+#define SMB_VFS_STAT vfs_stat
-+#define SMB_VFS_LISTADS vfs_listads
-+
-+/****************************************************************************
-+ ****************************************************************************
-+ Return a linked list of the alternate streams Plus the total size
-+****************************************************************************/
-+struct ads_list {
-+      struct ads_list *next, *prev;
-+      struct ads_struct ads;
-+};
-+
-+static struct ads_list *get_ads_list(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp, const char *fname, size_t *pads_total_len)
-+{
-+      /* Get a list of all ads with size, lax namesize is 64k. */
-+      size_t ads_namelist_size = 4096;
-+      char *ads_namelist;
-+      char *p;
-+      ssize_t sizeret;
-+      int i;
-+      struct ads_list *ads_list_head = NULL;
-+
-+      *pads_total_len = 0;
-+
-+      DEBUG(10,("get_ads_list\n" ));
-+      
-+      for (i = 0, ads_namelist = talloc(mem_ctx, ads_namelist_size); i < 6;
-+                      ads_namelist = talloc_realloc(mem_ctx, ads_namelist, ads_namelist_size), i++) {
-+
-+              sizeret = SMB_VFS_LISTADS(conn, fname, ads_namelist, ads_namelist_size);
-+              if (sizeret == -1 && errno == ERANGE) {
-+                      ads_namelist_size *= 2;
-+              } else {
-+                      break;
-+              }
-+      }
-+
-+      if (sizeret == -1)
-+              return NULL;
-+
-+      DEBUG(10,("get_ads_list: ads_namelist size = %d\n", sizeret ));
-+
-+      if (sizeret) { 
-+              for (p = ads_namelist; p - ads_namelist < sizeret; p += strlen(p) +1) {
-+                      struct ads_list *listp, *tmp;
-+                      SMB_STRUCT_STAT sbuf;
-+                      char *t;
-+                      
-+                      listp = talloc(mem_ctx, sizeof(struct ads_list));
-+                      if (!listp)
-+                              return NULL;
-+
-+                      listp->ads.name = talloc_strdup(mem_ctx, p);
-+                      if (!listp->ads.name)
-+                              return NULL;
-+                      
-+                      listp->ads.size = 0;
-+                      listp->ads.allocation_size = 0;
-+
-+                      t = talloc_asprintf(mem_ctx, "%s%s", fname, p);
-+                      if (!t)
-+                              return NULL;
-+                      if (!SMB_VFS_STAT(conn, t ,&sbuf)) {
-+                              listp->ads.size = get_file_size(sbuf);
-+                              listp->ads.allocation_size = get_allocation_size(NULL,&sbuf);
-+                      }
-+                      /* FIXME get ride of this */
-+                      {
-+                      fstring dos_ads_name;
-+                              
-+                              push_ascii_fstring(dos_ads_name, listp->ads.name);
-+                              *pads_total_len += strlen(dos_ads_name) + 1 + 24;
-+                              DEBUG(10,("get_ads_list: total_len = %u, %s, size = %llu\n",
-+                                              *pads_total_len, dos_ads_name, listp->ads.size ));
-+                      }
-+                      DLIST_ADD_END(ads_list_head, listp, tmp);
-+              }
-+      }
-+
-+      DEBUG(10,("get_ads_list: total_len = %u\n", *pads_total_len));
-+      return ads_list_head;
-+}
-+
-+/****************************************************************************
-+ Fill a qfilepathinfo buffer with alternate streams. 
-+ Returns the length of the buffer that was filled.
-+****************************************************************************/
-+
-+static unsigned int fill_ads_buffer(char *pdata, unsigned int total_data_size,
-+      connection_struct *conn, files_struct *fsp, const char *fname)
-+{
-+      unsigned int ret_data_size = 0;
-+      char *p = pdata;
-+      size_t total_ads_len;
-+      TALLOC_CTX *mem_ctx;
-+      struct ads_list *ads_list;
-+
-+      SMB_ASSERT(total_data_size >= 24);
-+
-+      mem_ctx = talloc_init(/*"fill_ads_buffer"*/);
-+      if (!mem_ctx) {
-+              return 0;
-+      }
-+
-+      ads_list = get_ads_list(mem_ctx, conn, fsp, fname, &total_ads_len);
-+      if (!ads_list) {
-+              talloc_destroy(mem_ctx);
-+              return 0;
-+      }
-+
-+      if (total_ads_len > total_data_size) {
-+              talloc_destroy(mem_ctx);
-+              return 0;
-+      }
-+
-+      for (p = pdata; ads_list; ads_list = ads_list->next) {
-+#if 0
-+              size_t dos_namelen;
-+              fstring dos_ads_name;
-+
-+              push_ascii_fstring(dos_ads_name, ads_list->ads.name);
-+              dos_namelen = strlen(dos_ads_name);
-+              if (dos_namelen > 255 || dos_namelen == 0) {
-+                      break;
-+              }
-+              if (dos_namelen + 24 > total_data_size) {
-+                      break;
-+              }
-+#endif
-+              /* We know we have room. */
-+              size_t byte_len = dos_PutUniCode(p +24, ads_list->ads.name, -1, False);
-+              size_t off = SMB_ROUNDUP(24 +byte_len, 8); 
-+              
-+              SIVAL(p,0,0); /* from ethereal next entry offset */
-+              SIVAL(p,4, byte_len); /* Byte length of unicode string :filename:$DATA */
-+              SOFF_T(p,8, ads_list->ads.size);
-+              SOFF_T(p,16, ads_list->ads.allocation_size);
-+              if (ads_list->next) {
-+                  SIVAL(p,0, off);
-+              }
-+              else {
-+                  /* don't pad the last one */
-+                  off = 24 +byte_len;
-+              }
-+
-+              total_data_size -= off;
-+              p += off;
-+      }
-+
-+      ret_data_size = PTR_DIFF(p, pdata);
-+      DEBUG(10,("fill_ads_buffer: data_size = %u, total_ads_len = %u\n",
-+                      ret_data_size, total_ads_len ));
-+      talloc_destroy(mem_ctx);
-+      return ret_data_size;
-+}
-+
- /****************************************************************************
-   Send the required number of replies back.
-   We assume all fields other than the data fields are
-@@ -1934,7 +2090,7 @@
-                               break;
-                       }
-               
--#if 0
-+#if 1
-               /*
-                * NT4 server just returns "invalid query" to this - if we try to answer
-                * it then NTws gets a BSOD! (tridge).
-@@ -1943,16 +2099,24 @@
-               case SMB_QUERY_FILE_STREAM_INFO:
- #endif
-               case SMB_FILE_STREAM_INFORMATION:
--                      if (mode & aDIR) {
--                              data_size = 0;
--                      } else {
--                              size_t byte_len = dos_PutUniCode(pdata+24,"::$DATA", 0xE, False);
--                              SIVAL(pdata,0,0); /* ??? */
--                              SIVAL(pdata,4,byte_len); /* Byte length of unicode string ::$DATA */
--                              SOFF_T(pdata,8,file_size);
--                              SIVAL(pdata,16,allocation_size);
--                              SIVAL(pdata,20,0); /* ??? */
--                              data_size = 24 + byte_len;
-+                      {
-+                              size_t off;
-+
-+                              if (mode & aDIR) {
-+                                      off = 0;
-+                              } else {
-+                                      size_t byte_len = dos_PutUniCode(pdata+24,"::$DATA", 0xE, False);
-+                              
-+                                      off = SMB_ROUNDUP(24 +byte_len, 8); /* FIXME or 8 ? */
-+                                      SIVAL(pdata,0,0); /* from ethereal next entry offset */
-+                                      SIVAL(pdata,4,byte_len); /* Byte length of unicode string ::$DATA */
-+                                      SOFF_T(pdata,8,file_size);
-+                                      SOFF_T(pdata,16,allocation_size);
-+                              }
-+                              if ((data_size = fill_ads_buffer(pdata +off, data_size, conn, fsp, fname))) {
-+                                      SIVAL(pdata,0,off);
-+                              }
-+                              data_size += off;
-                       }
-                       break;
-diff -ur ../smb3.0a20.orig/source/smbd/vfs-wrap.c ./source/smbd/vfs-wrap.c
---- ../smb3.0a20.orig/source/smbd/vfs-wrap.c   Mon Jan  6 18:05:49 2003
-+++ ./source/smbd/vfs-wrap.c   Fri Jun  4 16:33:16 2004
-@@ -739,3 +739,14 @@
- {
-       return sys_acl_free_qualifier(qualifier, tagtype);
- }
-+
-+/****************************************************************
-+ Alternate stream operations.
-+*****************************************************************/
-+
-+ssize_t vfswrap_listads(/* struct vfs_handle_struct *handle, */ struct connection_struct *conn,const char *path, char *list, size_t size)
-+{
-+        errno = ENOSYS;
-+        return -1;
-+}
-+
-Only in ./source/smbd: vfs-wrap.c.orig
-diff -ur ../smb3.0a20.orig/source/smbd/vfs.c ./source/smbd/vfs.c
---- ../smb3.0a20.orig/source/smbd/vfs.c        Mon Jan  6 18:05:48 2003
-+++ ./source/smbd/vfs.c        Fri Jun  4 05:40:09 2004
-@@ -124,7 +124,10 @@
-       vfswrap_sys_acl_get_perm,
-       vfswrap_sys_acl_free_text,
-       vfswrap_sys_acl_free_acl,
--      vfswrap_sys_acl_free_qualifier
-+      vfswrap_sys_acl_free_qualifier,
-+
-+      /* alternate streams operations. */
-+      vfswrap_listads
- };
- /****************************************************************************
diff --git a/contrib/patches/patch.vfs b/contrib/patches/patch.vfs
deleted file mode 100644 (file)
index 06295d0..0000000
+++ /dev/null
@@ -1,1115 +0,0 @@
-diff -Nur vfs/Makefile vfs.new/Makefile
---- vfs/Makefile       Thu Jan  1 00:00:00 1970
-+++ vfs.new/Makefile   Mon Jul 12 10:48:56 2004
-@@ -0,0 +1,40 @@
-+##########################################################################
-+# Makefile for Samba VFS modules 
-+###########################################################################
-+
-+CC=gcc -g
-+LIBTOOL=/usr/bin/libtool
-+# REPLACE with samba source 
-+SMB=/u/redhat/paris/cvs/samba/smb3.0a20
-+
-+# REPLACE with samba build folder
-+BUILD=/mnt/hdd/build/smb.1.3
-+
-+CFLAGS=-Wall -I $(BUILD)/include \
-+-I$(SMB)/source -I$(SMB)/source/include -I$(SMB)/source/ubiqx -I$(SMB)/source/smbwrapper
-+
-+
-+LDFLAGS=-shared
-+
-+VFS_OBJS=vfs_ads.so
-+
-+SHELL=/bin/sh
-+
-+default: $(VFS_OBJS)
-+
-+# Pattern rules
-+
-+%.so: %.lo
-+      @echo Linking $<
-+      @$(LIBTOOL) --mode=link $(CC) -o $@ $< $(LDFLAGS)
-+
-+%.lo: %.c
-+      @echo Compiling $<
-+      @$(LIBTOOL) --mode=compile $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
-+
-+# Misc targets
-+
-+clean:
-+      rm -rf .libs */.libs
-+      rm -f core *~ *% *.bak *.o */*.o *.lo $(VFS_OBJS)
-+
-diff -Nur vfs/README vfs.new/README
---- vfs/README Thu Jan  1 00:00:00 1970
-+++ vfs.new/README     Tue Jul 13 02:28:21 2004
-@@ -0,0 +1,34 @@
-+This a vfs for NT ADS 
-+you must set SMB and BUILD variables in Makefile.
-+
-+old smb.conf
-+[test_ads]
-+   comment = test ADS Mac/PC directory
-+   path=/home/test_ads/
-+#  /.AppleD* is mandatory 
-+   veto files = /.AppleD*/Network Trash Folder/Icon\r/
-+   delete veto files = True
-+# full path to vfs_ads.so 
-+   vfs object = /usr/src/samba/vfs/vfs_ads.so
-+   browseable = yes
-+   writable = yes
-+
-+new one (current svn tree)
-+copy vfs_ads.so as ads.so in <prefix>/lib/vfs/
-+eg
-+cp vfs_ads.so /opt/lib/vfs/ads.so
-+
-+smb.conf
-+[test_ads]
-+   comment = test ADS Mac/PC directory
-+   path=/home/test_ads/
-+ 
-+#  /.AppleD* is mandatory 
-+   veto files = /.AppleD*/Network Trash Folder/Icon\r/
-+   delete veto files = True
-+   vfs objects = ads
-+   browseable = yes
-+   writable = yes
-+
-+
-+Didier
-diff -Nur vfs/vfs_ads.c vfs.new/vfs_ads.c
---- vfs/vfs_ads.c      Thu Jan  1 00:00:00 1970
-+++ vfs.new/vfs_ads.c  Wed Jul 14 16:37:15 2004
-@@ -0,0 +1,1029 @@
-+/* 
-+ * CAP VFS module for Samba 3.x Version 0.3
-+ *
-+ * Copyright (C) Tim Potter, 1999-2000
-+ * Copyright (C) Alexander Bokovoy, 2002-2003
-+ * Copyright (C) Stefan (metze) Metzmacher, 2003
-+ * Copyright (C) TAKAHASHI Motonobu (monyo), 2003
-+ *
-+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-+ *
-+ * modified for alternate data stream
-+ * Copyright (C) Didier Gautheron 2004
-+ * 
-+ * this module should compile with old 3.0 API and 2004-07 svn API
-+ */
-+
-+
-+#include "includes.h"
-+
-+#undef DBGC_CLASS
-+#define DBGC_CLASS DBGC_VFS
-+
-+#define ADS_FOLDER    ".AppleDouble"
-+#define ADOUBLEMODE   0777
-+
-+/* FIXME found a better test */
-+#ifdef        SMB_VFS_OP
-+#define ADS_NEW_MODULE
-+
-+/* for current svn tree */
-+#define ADS_TALLOC_INIT(a) talloc_init(a)
-+
-+#define HANDLE_PARAMETER vfs_handle_struct *handle,
-+#define HANDLE handle,
-+
-+/* ------------------- */
-+#else
-+
-+#define ADS_TALLOC_INIT(a) talloc_init()
-+
-+#define HANDLE_PARAMETER 
-+#define HANDLE
-+
-+/* VFS operations */
-+static struct vfs_ops default_vfs_ops;   /* For passthrough operation */
-+static struct smb_vfs_handle_struct *ads_handle; 
-+
-+#define SMB_VFS_NEXT_DISK_FREE(a,b,c,d,e,f,g)       default_vfs_ops.disk_free(b,c,d,e,f,g)
-+#define SMB_VFS_NEXT_OPENDIR(a,b,c)                 default_vfs_ops.opendir(b,c)
-+#define SMB_VFS_NEXT_READDIR(a,b,c)                 default_vfs_ops.readdir(b,c)
-+#define SMB_VFS_NEXT_MKDIR(a,b,c,d)                 default_vfs_ops.mkdir(b,c,d)
-+#define SMB_VFS_NEXT_RMDIR(a,b,c)                   default_vfs_ops.rmdir(b,c)
-+#define SMB_VFS_NEXT_OPEN(a,b,c,d,e)                default_vfs_ops.open(b,c,d,e)
-+#define SMB_VFS_NEXT_RENAME(a,b,c,d)                default_vfs_ops.rename(b,c,d)
-+#define SMB_VFS_NEXT_STAT(a,b,c,d)                  default_vfs_ops.stat(b,c,d)
-+#define SMB_VFS_NEXT_LSTAT(a,b,c,d)                 default_vfs_ops.lstat(b,c,d)
-+#define SMB_VFS_NEXT_UNLINK(a,b,c)                  default_vfs_ops.unlink(b,c)
-+#define SMB_VFS_NEXT_CHMOD(a,b,c,d)                 default_vfs_ops.chmod(b,c,d)
-+#define SMB_VFS_NEXT_CHOWN(a,b,c,d,e)               default_vfs_ops.chown(b,c,d,e) 
-+#define SMB_VFS_NEXT_CHDIR(a,b,c)                   default_vfs_ops.chdir(b,c)
-+#define SMB_VFS_NEXT_UTIME(a,b,c,d)                 default_vfs_ops.utime(b,c,d)
-+#define SMB_VFS_NEXT_SYMLINK(a,b,c,d)               default_vfs_ops.symlink(b,c,d)
-+#define SMB_VFS_NEXT_READLINK(a,b,c,d,e)            default_vfs_ops.readlink(b,c,d,e)
-+#define SMB_VFS_NEXT_LINK(a,b,c,d)                  default_vfs_ops.link(b,c,d)
-+#define SMB_VFS_NEXT_MKNOD(a,b,c,d,e)               default_vfs_ops.mknod(b,c,d,e)
-+#define SMB_VFS_NEXT_REALPATH(a,b,c,d)              default_vfs_ops.realpath(b,c,d)
-+#define SMB_VFS_NEXT_SET_NT_ACL(a,b,c,d,e)          default_vfs_ops.set_nt_acl(b,c,d,e)
-+#define SMB_VFS_NEXT_CHMOD_ACL(a,b,c,d)             default_vfs_ops.chmod_acl(b,c,d)
-+#define SMB_VFS_NEXT_SYS_ACL_GET_FILE(a,b,c,d)      default_vfs_ops.sys_acl_get_file(b,c,d)
-+#define SMB_VFS_NEXT_SYS_ACL_SET_FILE(a,b,c,d,e)    default_vfs_ops.sys_acl_set_file(b,c,d,e)
-+#define SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(a,b,c) default_vfs_ops.sys_acl_delete_def_file(b,c)
-+/* ads functions */
-+
-+#endif
-+
-+/* ------------------------- 
-+ * format
-+ * .AppleDouble/filename/stream name
-+ * 
-+ * return the *LAST* '/' in path
-+ */
-+static int ads_get_path_ptr(char *path)
-+{
-+      int i   = 0;
-+      int ptr = 0;
-+      
-+      for (i = 0; path[i]; i ++) {
-+              if (path[i] == '/')
-+                      ptr = i;
-+      }
-+      
-+      return ptr;
-+}
-+
-+/* ------------------------------
-+ * return the *FIRST* ':' in path
-+*/
-+static int ads_get_stream_ptr(const char *path)
-+{
-+      int i   = 0;
-+      int ptr = 0;
-+      
-+      for (i = 0; path[i]; i ++) {
-+              if (path[i] == ':') {
-+                      ptr = i;
-+                      break;
-+              }
-+      }
-+      return ptr;
-+}
-+
-+/* ---------------- 
-+ * fname is only a filename
-+*/
-+
-+static char *ads_canonical_dir(TALLOC_CTX *ctx, const char *path, const char *fname, int isdir)
-+{
-+    if (isdir) {
-+        return talloc_asprintf(ctx, "%s/%s/%s/.Parent", path, fname, ADS_FOLDER);
-+    }
-+    return talloc_asprintf(ctx, "%s/%s/%s", path, ADS_FOLDER, fname);
-+    
-+}
-+
-+/* ---------------- 
-+ * return directory pathname for an alternate data stream
-+ * fname is *NOT* an altername name (ie foo:bar)
-+*/
-+static char *ads_dir(TALLOC_CTX *ctx, const char *path, const char *fname, int isdir)
-+{
-+    int ptr0 = 0;
-+    int ptr1 = 0;
-+    char *temp;
-+
-+#if 0
-+    if (fname[0] == '.') ptr0 ++;
-+    if (fname[1] == '/') ptr0 ++;
-+#endif    
-+    temp = talloc_asprintf(ctx, "%s/%s", path, &fname[ptr0]);
-+    ptr1 = ads_get_path_ptr(temp);
-+    temp[ptr1] = '\0';
-+    return ads_canonical_dir(ctx, temp, &temp[ptr1 + 1], isdir);
-+}
-+
-+/* ----------------------------------
-+ * build the pathname for stream, create folder if (mode & O_CREAT)
-+ * return -1 on error
-+ * 0 it's not a stream
-+ * 1 it's a stream
-+ *
-+ * main_path : file fullpathname with :$DATA removed
-+ * ads_path: unix pathname 
-+ * if it's not an ADS then main_path == ads_path
-+ *
-+ */
-+static int ads_build_paths(TALLOC_CTX *ctx, const char *path, const char *fname,
-+                              char **ads_path, char **main_path, SMB_STRUCT_STAT **main_info, int flag)
-+{
-+        int ret = 0;
-+      int ptr0 = 0;
-+      int ptr1 = 0;
-+      int ptr2 = 0;
-+      int ptr3 = 0;
-+      char *dname = 0;
-+      char *name  = 0;
-+      SMB_STRUCT_STAT ads_info;
-+
-+      if (!ctx || !path || !fname || !ads_path || !main_path || !main_info || !*main_info)
-+              return -1;
-+#if 1
-+      DEBUG(3, ("ADS: PATH: %s[%s]\n", path, fname));
-+#endif
-+      if (strstr(path, ADS_FOLDER) || strstr(fname, ADS_FOLDER)) {
-+              DEBUG(1, ("ADS: path %s[%s] already contains %s\n", path, fname, ADS_FOLDER));
-+              return -1;
-+      }
-+
-+#if 0
-+      if (fname[0] == '.') ptr0 ++;
-+      if (fname[1] == '/') ptr0 ++;
-+#endif
-+
-+      *main_path = talloc_asprintf(ctx, "%s/%s", path, &fname[ptr0]);
-+      *ads_path = NULL;
-+
-+      /* get pointer to last '/' */
-+      ptr1 = ads_get_path_ptr(*main_path);
-+      ptr2 = ads_get_stream_ptr(*main_path +ptr1 +1);
-+      /* FIXME
-+       * what about ::$DATA or :name:$DATA
-+      */
-+
-+      if (ptr2) {
-+          /* it's an alternate stream */
-+          ptr2 += ptr1 +1;
-+          (*main_path)[ptr2] = 0;
-+          ptr3 = ads_get_stream_ptr(*main_path +ptr2 +1);
-+          if (ptr3) {
-+              ptr3 += ptr2 +1;
-+              /* check it's $DATA */
-+              if (!strcmp("$DATA", &(*main_path)[ptr3+1])) {
-+                  (*main_path)[ptr3] = 0;
-+              }
-+          }
-+
-+          DEBUG(3, ("ADS: MAIN DATA %s\n", *main_path));
-+
-+          if (sys_lstat(*main_path, *main_info) < 0) {
-+              /* if we can't get the main file give up */
-+              return -1;
-+          }
-+          (*main_path)[ptr2] = ':';
-+          dname = talloc_strdup(ctx, *main_path);
-+          dname[ptr1] = '\0'; 
-+          name = *main_path;
-+          name[ptr2] = '\0';
-+          if (S_ISDIR((*main_info)->st_mode)) {
-+              *ads_path = talloc_asprintf(ctx, "%s/%s/%s/.Parent/%s", dname, &name[ptr1 + 1], ADS_FOLDER, &name[ptr2 + 1]);
-+          }
-+          else {
-+              *ads_path = talloc_asprintf(ctx, "%s/%s/%s/%s", dname, ADS_FOLDER, &name[ptr1 + 1], &name[ptr2 + 1]);
-+          }
-+          /* XXX are we always the right user ?*/
-+          if (sys_lstat(*ads_path, &ads_info) < 0) {
-+              int st_ret;
-+              /* */
-+              if (errno == ENOENT && (flag & O_CREAT))  {
-+                  char *ads_base = ads_canonical_dir(ctx, dname, &name[ptr1 + 1], S_ISDIR((*main_info)->st_mode));
-+                  mode_t mode;
-+                  
-+                  st_ret = mkdir(ads_base, 0777);
-+                  if (st_ret < 0) {
-+                      if (errno == ENOENT) {
-+                          char *ads_double;
-+                          if (S_ISDIR((*main_info)->st_mode)) {
-+                              ads_double = talloc_asprintf(ctx, "%s/%s/%s", dname, &name[ptr1 + 1], ADS_FOLDER);
-+                          }
-+                          else {
-+                              ads_double = talloc_asprintf(ctx, "%s/%s", dname, ADS_FOLDER);
-+                          }
-+                          if (mkdir(ads_double, 0777) < 0)
-+                              return -1;
-+                          if ((st_ret = mkdir(ads_base, 0777)) < 0)
-+                              return -1;
-+                          
-+                          /* we just created .AppleDouble/file/ update mode with dir search 
-+                           * XXX what about acl?
-+                          */
-+                          mode = (*main_info)->st_mode;
-+                          if ((mode & (S_IRUSR | S_IWUSR )))
-+                              mode |= S_IXUSR;
-+                          if ((mode & (S_IRGRP | S_IWGRP )))
-+                              mode |= S_IXGRP;
-+                          if ((mode & (S_IROTH | S_IWOTH ))) 
-+                              mode |= S_IXOTH;
-+                          chmod(ads_base, mode);
-+                      }
-+                      else 
-+                          errno = ENOENT;
-+                  }
-+              }
-+              else 
-+                  return -1;
-+          }
-+          ret = 1;
-+      }
-+      else {
-+          *ads_path = *main_path;
-+          if (sys_lstat(*main_path, *main_info) < 0) {
-+              *main_info = NULL;
-+          }
-+      }
-+#if 1
-+      DEBUG(3, ("ADS: DEBUG:[%s] [%s]\n", *main_path, *ads_path)); 
-+#endif
-+      return ret;
-+}
-+
-+/* ------------------------ */
-+static SMB_BIG_UINT ads_disk_free(HANDLE_PARAMETER connection_struct *conn, const char *path,
-+      BOOL small_query, SMB_BIG_UINT *bsize,
-+      SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
-+{
-+      return SMB_VFS_NEXT_DISK_FREE(handle, conn, path, small_query, bsize, dfree, dsize);
-+}
-+
-+static DIR *ads_opendir(HANDLE_PARAMETER connection_struct *conn, const char *fname)
-+{
-+      return SMB_VFS_NEXT_OPENDIR(handle, conn, fname);
-+}
-+
-+static struct dirent *ads_readdir(HANDLE_PARAMETER connection_struct *conn, DIR *dirp)
-+{
-+        struct dirent *result;
-+      DEBUG(3,("ads: ads_readdir\n"));
-+      result = SMB_VFS_NEXT_READDIR(handle, conn, dirp);
-+      if (result) {
-+        DEBUG(3,("ads: ads_readdir: %s\n", result->d_name));
-+        }
-+        return result;
-+}
-+
-+/* ------------------------- */
-+static int ads_mkdir(HANDLE_PARAMETER connection_struct *conn, const char *path, mode_t mode)
-+{
-+      return SMB_VFS_NEXT_MKDIR(handle, conn, path, mode);
-+}
-+
-+/* ------------------------- */
-+static int unlink_file(const char *path)
-+{
-+int ret = 0;
-+         
-+      become_root();
-+        ret = unlink(path);
-+        unbecome_root();
-+        return ret;
-+}
-+
-+/* ------------------------- */
-+static int unlink_folder(const char *path)
-+{
-+int ret = 0;
-+         
-+      become_root();
-+        ret = rmdir(path);
-+        unbecome_root();
-+        return ret;
-+}
-+
-+/* ------------------------- 
-+   remove all files in an AppleDouble folder
-+*/
-+static void rrmdir(TALLOC_CTX *ctx, char *path)
-+{
-+        int n;
-+        char *dpath;
-+        struct dirent **namelist;
-+ 
-+        if (!path) return;
-+ 
-+        n = scandir(path, &namelist, 0, alphasort);
-+        if (n < 0) {
-+                return;
-+        } 
-+        while (n --) {
-+              if (strcmp(namelist[n]->d_name, ".") == 0 || strcmp(namelist[n]->d_name, "..") == 0) {
-+                      free(namelist[n]);
-+                        continue;
-+                }
-+                if ((dpath = talloc_asprintf(ctx, "%s/%s",path, namelist[n]->d_name))) {
-+                      unlink_file(dpath);
-+                }
-+                free(namelist[n]);
-+        }
-+        free(namelist);
-+        unlink_folder(path);
-+}
-+
-+/* --------------------------- */
-+static void rrm_adsdir(TALLOC_CTX *ctx, char *path)
-+{
-+        int n;
-+        char *dpath;
-+        struct dirent **namelist;
-+ 
-+        if (!path) return;
-+ 
-+        n = scandir(path, &namelist, 0, alphasort);
-+        if (n < 0) {
-+                return;
-+        } 
-+        while (n --) {
-+              if (strcmp(namelist[n]->d_name, ".") == 0 || strcmp(namelist[n]->d_name, "..") == 0) {
-+                      free(namelist[n]);
-+                        continue;
-+                }
-+                if ((dpath = talloc_asprintf(ctx, "%s/%s",path, namelist[n]->d_name))) {
-+                      rrmdir(ctx, dpath);
-+                }
-+                free(namelist[n]);
-+        }
-+        free(namelist);
-+        unlink_folder(path);
-+}
-+
-+/* ------------------------- 
-+ * XXX 
-+ * if in smb.conf there's :
-+ * delete veto files = True
-+ * veto files = /.AppleD* /
-+*/
-+static int ads_rmdir( HANDLE_PARAMETER connection_struct *conn, const char *path)
-+{
-+        BOOL add = False;
-+        TALLOC_CTX *ctx = 0;
-+        char *dpath;
-+        int  ret = 0;
-+ 
-+        if (!conn || !conn->origpath || !path) goto exit_rmdir;
-+
-+      /* .AppleD* */
-+        strstr(path, ADS_FOLDER) ? (add = False) : (add = True);
-+ 
-+        if (!(ctx = ADS_TALLOC_INIT("ads_rmdir")))
-+                goto exit_rmdir;
-+ 
-+        if (!(dpath = talloc_asprintf(ctx, "%s/%s%s",conn->origpath, path, add ? "/"ADS_FOLDER : "")))
-+              goto exit_rmdir;
-+              
-+      /* remove folder .AppleDouble */
-+        rrm_adsdir(ctx, dpath);
-+ 
-+exit_rmdir:
-+      ret = SMB_VFS_NEXT_RMDIR(handle, conn, path);
-+        talloc_destroy(ctx);
-+
-+      return ret;
-+}
-+
-+/* ------------------------- */
-+static int ads_open(HANDLE_PARAMETER connection_struct *conn, const char *fname, int flags, mode_t mode)
-+{
-+      int ret = 0;
-+      char *ads_path = 0;
-+      char *main_path = 0;
-+      TALLOC_CTX *ctx;
-+      SMB_STRUCT_STAT st;
-+      SMB_STRUCT_STAT *main_info = &st;
-+      
-+      DEBUG(3,("ads: ads_open for %s %x\n", fname, flags));
-+      if (!(ctx = ADS_TALLOC_INIT("ads_open")))
-+              return -1;
-+      /* convert to */
-+      if (ads_build_paths(ctx, conn->origpath, fname, &ads_path, &main_path, &main_info, flags) < 0) {
-+          talloc_destroy(ctx);
-+          return -1;
-+      }
-+
-+      ret = SMB_VFS_NEXT_OPEN(handle, conn, ads_path, flags, mode);
-+      talloc_destroy(ctx);
-+      return ret;
-+
-+}
-+
-+static int isDir(SMB_STRUCT_STAT *st)
-+{
-+     if (st == NULL) {
-+         return 0;
-+     }
-+     return S_ISDIR(st->st_mode);
-+}
-+
-+/* ------------------------- */
-+static int ads_rename(HANDLE_PARAMETER connection_struct *conn, const char *old, const char *new)
-+{
-+      int ret = 0;
-+      TALLOC_CTX *ctx;
-+      char *ads_path = 0;
-+      char *main_path = 0;
-+      SMB_STRUCT_STAT st;
-+      SMB_STRUCT_STAT *main_info = &st;
-+      
-+      DEBUG(3,("ads: ads_rename %s --> %sx\n", old, new));
-+      
-+      if (!(ctx = ADS_TALLOC_INIT("ads_rename")))
-+          return -1;
-+
-+      if (ads_build_paths(ctx, conn->origpath, old, &ads_path, &main_path, &main_info, 0) < 0) {
-+          talloc_destroy(ctx);
-+          return -1;
-+      }
-+
-+      if (ads_path != main_path) {
-+          /* you can't rename an ads ! */
-+          talloc_destroy(ctx);
-+          errno = EINVAL;
-+          return -1;
-+      }
-+
-+      ret = SMB_VFS_NEXT_RENAME(handle, conn, old, new);
-+      if (!ret && !isDir(main_info)) {
-+          int  ptr1;
-+          int  ptr2;
-+          
-+          char *ads_old  = ads_dir(ctx, conn->origpath, old, 0);
-+          char *ads_new  = ads_dir(ctx, conn->origpath, new, 0);
-+
-+          /* is dest folder .Adouble there ? */
-+          ptr1 = ads_get_path_ptr(ads_new);
-+          ptr2 = ads_get_path_ptr(ads_old);
-+
-+          ads_new[ptr1] = '\0';
-+          ads_old[ptr2] = '\0';
-+          if (strcmp(ads_new, ads_old)) {
-+              mkdir(ads_new, 0777);
-+          }
-+
-+          ads_new[ptr1] = '/';
-+          ads_old[ptr2] = '/';
-+          
-+          SMB_VFS_NEXT_RENAME(handle, conn, ads_old, ads_new);
-+      }
-+
-+      talloc_destroy(ctx);
-+      return ret;
-+}
-+
-+/* ------------------------- 
-+ * For an ADS what do we need to return , ADS ? main DATA?
-+*/
-+static int ads_stat(HANDLE_PARAMETER connection_struct *conn, const char *fname, SMB_STRUCT_STAT *sbuf)
-+{
-+      int ret = 0;
-+      char *ads_path = 0;
-+      char *main_path = 0;
-+      TALLOC_CTX *ctx;
-+      SMB_STRUCT_STAT st;
-+      SMB_STRUCT_STAT *main_info = &st;
-+
-+      DEBUG(3,("ads: ads_stat for %s\n", fname));
-+
-+      if (!(ctx = ADS_TALLOC_INIT("ads_stat")))
-+          return -1;
-+      /* which inode ?
-+      */
-+      if (ads_build_paths(ctx, conn->origpath, fname, &ads_path, &main_path, &main_info, 0) < 0) {
-+          talloc_destroy(ctx);
-+          return -1;
-+      }
-+
-+      ret = SMB_VFS_NEXT_STAT(handle, conn, ads_path, sbuf);
-+      talloc_destroy(ctx);
-+      return ret;
-+}
-+
-+/* ------------------------- */
-+static int ads_lstat(HANDLE_PARAMETER connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
-+{
-+      int ret = 0;
-+      char *ads_path = 0;
-+      char *main_path = 0;
-+      TALLOC_CTX *ctx;
-+      SMB_STRUCT_STAT st;
-+      SMB_STRUCT_STAT *main_info = &st;
-+      
-+      if (!(ctx = ADS_TALLOC_INIT("ads_lstat")))
-+              return -1;
-+      /* which inode ?
-+      */
-+      if (ads_build_paths(ctx, conn->origpath, path, &ads_path, &main_path, &main_info, 0) < 0) {
-+          talloc_destroy(ctx);
-+          return -1;
-+      }
-+
-+      return SMB_VFS_NEXT_LSTAT(handle, conn, ads_path, sbuf);
-+      talloc_destroy(ctx);
-+      return ret;
-+}
-+
-+/* ------------------------- */
-+static int ads_unlink(HANDLE_PARAMETER connection_struct *conn, const char *path)
-+{
-+      int ret = 0;
-+
-+      char *ads_path = 0;
-+      char *main_path = 0;
-+      TALLOC_CTX *ctx;
-+      SMB_STRUCT_STAT st;
-+      SMB_STRUCT_STAT *main_info = &st;
-+      
-+      DEBUG(3,("ads: ads_unlink %s\n", path));
-+      if (!(ctx = ADS_TALLOC_INIT("ads_unlink")))
-+              return -1;
-+
-+      if (ads_build_paths(ctx, conn->origpath, path, &ads_path, &main_path, &main_info, 0) < 0) {
-+          talloc_destroy(ctx);
-+          return -1;
-+      }
-+
-+      ret = SMB_VFS_NEXT_UNLINK(handle, conn, ads_path);
-+      /*
-+         if data stream
-+            for each stream
-+                unlink
-+      */
-+      if (!ret && ads_path == main_path) {
-+          char *ads_base  = ads_dir(ctx, conn->origpath, path, isDir(main_info));
-+          struct dirent *dent = 0;
-+          DIR *dir = opendir(ads_base);
-+          
-+          if (dir) {
-+              char *dpath;
-+              
-+              while (NULL != (dent = readdir(dir))) {
-+                  if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
-+                      continue;
-+                  if (!(dpath = talloc_asprintf(ctx, "%s/%s", ads_base, dent->d_name)))
-+                      continue;
-+                  /* XXX need to be root ?  */
-+                  SMB_VFS_NEXT_UNLINK(handle, conn, dpath);               
-+              }
-+              closedir(dir);
-+              rmdir(ads_base);
-+          }
-+      }
-+
-+      talloc_destroy(ctx);
-+      return ret;
-+}
-+
-+/* ------------------------- */
-+static int ads_chmod(HANDLE_PARAMETER connection_struct *conn, const char *path, mode_t mode)
-+{
-+      int ret = 0;
-+      char *ads_path = 0;
-+      char *main_path = 0;
-+      TALLOC_CTX *ctx;
-+      SMB_STRUCT_STAT st;
-+      SMB_STRUCT_STAT *main_info = &st;
-+
-+      DEBUG(3,("ads: ads_chmod %s\n", path));
-+      /* if stream 
-+             error ?, change only the stream
-+      */
-+      if (!(ctx = ADS_TALLOC_INIT("ads_chmod")))
-+              return -1;
-+
-+      if (ads_build_paths(ctx, conn->origpath, path, &ads_path, &main_path, &main_info, 0) < 0) {
-+          talloc_destroy(ctx);
-+          return -1;
-+      }
-+
-+      ret = SMB_VFS_NEXT_CHMOD(handle, conn, ads_path, mode);
-+      /*
-+         if data stream
-+            for each stream
-+                chmod
-+      */
-+      if (!ret && ads_path == main_path) {
-+          char *ads_base  = ads_dir(ctx, conn->origpath, path, isDir(main_info));
-+          struct dirent *dent = 0;
-+          DIR *dir = opendir(ads_base);
-+          
-+          if (dir) {
-+              char *dpath;
-+              
-+              while (NULL != (dent = readdir(dir))) {
-+                  if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
-+                      continue;
-+                  if (!(dpath = talloc_asprintf(ctx, "%s/%s", ads_base, dent->d_name)))
-+                      continue;
-+                  /* XXX need to be root ? */
-+                  SMB_VFS_NEXT_CHMOD(handle, conn, dpath, mode);
-+              }
-+              closedir(dir);
-+              /* XXX need to change ads_base too*/
-+          }
-+      }
-+
-+      talloc_destroy(ctx);
-+      return ret;
-+}
-+
-+/* ------------------------- */
-+static int ads_chown(HANDLE_PARAMETER connection_struct *conn, const char *path, uid_t uid, gid_t gid)
-+{
-+      int ret = 0;
-+      char *ads_path = 0;
-+      char *main_path = 0;
-+      TALLOC_CTX *ctx;
-+      SMB_STRUCT_STAT st;
-+      SMB_STRUCT_STAT *main_info = &st;
-+
-+      DEBUG(3,("ads: ads_chown %s\n", path));
-+      /* if stream 
-+             error ?, change only the stream
-+      */
-+      if (!(ctx = ADS_TALLOC_INIT("ads_chown")))
-+              return -1;
-+
-+      if (ads_build_paths(ctx, conn->origpath, path, &ads_path, &main_path, &main_info, 0) < 0) {
-+          talloc_destroy(ctx);
-+          return -1;
-+      }
-+
-+      ret = SMB_VFS_NEXT_CHOWN(handle, conn, ads_path, uid, gid);
-+      /* if data stream
-+            for each stream
-+                chmod
-+      */
-+      if (!ret && ads_path == main_path) {
-+          char *ads_base  = ads_dir(ctx, conn->origpath, path, isDir(main_info));
-+          struct dirent *dent = 0;
-+          DIR *dir = opendir(ads_base);
-+          
-+          if (dir) {
-+              char *dpath;
-+              
-+              while (NULL != (dent = readdir(dir))) {
-+                  if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
-+                      continue;
-+                  if (!(dpath = talloc_asprintf(ctx, "%s/%s", ads_base, dent->d_name)))
-+                      continue;
-+                  /* XXX need to be root ?, what do we do in case of error? */
-+                  SMB_VFS_NEXT_CHOWN(handle, conn, dpath, uid, gid);
-+              }
-+              closedir(dir);
-+              SMB_VFS_NEXT_CHOWN(handle, conn, ads_path, uid, gid);
-+          }
-+      }
-+
-+      talloc_destroy(ctx);
-+      return ret;
-+}
-+
-+/* ------------------------- */
-+static int ads_chdir(HANDLE_PARAMETER connection_struct *conn, const char *path)
-+{
-+      DEBUG(3,("ads: ads_chdir for %s\n", path));
-+      return SMB_VFS_NEXT_CHDIR(handle, conn, path);
-+}
-+
-+static int ads_utime(HANDLE_PARAMETER connection_struct *conn, const char *path, struct utimbuf *times)
-+{
-+      return SMB_VFS_NEXT_UTIME(handle, conn, path, times);
-+}
-+
-+
-+static BOOL ads_symlink(HANDLE_PARAMETER connection_struct *conn, const char *oldpath, const char *newpath)
-+{
-+      return SMB_VFS_NEXT_SYMLINK(handle, conn, oldpath, newpath);
-+}
-+
-+static BOOL ads_readlink(HANDLE_PARAMETER connection_struct *conn, const char *path, char *buf, size_t bufsiz)
-+{
-+      return SMB_VFS_NEXT_READLINK(handle, conn, path, buf, bufsiz);
-+}
-+
-+static int ads_link( HANDLE_PARAMETER connection_struct *conn, const char *oldpath, const char *newpath)
-+{
-+      return SMB_VFS_NEXT_LINK(handle, conn, oldpath, newpath);
-+}
-+
-+static int ads_mknod(HANDLE_PARAMETER connection_struct *conn, const char *path, mode_t mode, SMB_DEV_T dev)
-+{
-+      return SMB_VFS_NEXT_MKNOD(handle, conn, path, mode, dev);
-+}
-+
-+static char *ads_realpath(HANDLE_PARAMETER connection_struct *conn, const char *path, char *resolved_path)
-+{
-+      return SMB_VFS_NEXT_REALPATH(handle, conn, path, resolved_path);
-+}
-+
-+static BOOL ads_set_nt_acl(HANDLE_PARAMETER files_struct *fsp, const char *name, uint32 security_info_sent, struct security_descriptor_info *psd)
-+{
-+      return SMB_VFS_NEXT_SET_NT_ACL(handle, fsp, name, security_info_sent, psd);
-+}
-+
-+static int ads_chmod_acl(HANDLE_PARAMETER connection_struct *conn, const char *name, mode_t mode)
-+{
-+      /* If the underlying VFS doesn't have ACL support... */
-+#ifdef ADS_NEW_MODULE
-+        if (!handle->vfs_next.ops.chmod_acl) {                                                                          
-+#else
-+      if (!default_vfs_ops.chmod_acl) {
-+#endif
-+              errno = ENOSYS;
-+              return -1;
-+      }
-+      return SMB_VFS_NEXT_CHMOD_ACL(handle, conn, name, mode);
-+}
-+
-+static SMB_ACL_T ads_sys_acl_get_file(HANDLE_PARAMETER connection_struct *conn, const char *path_p, SMB_ACL_TYPE_T type)
-+{
-+      return SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, conn, path_p, type);
-+}
-+
-+static int ads_sys_acl_set_file(HANDLE_PARAMETER connection_struct *conn, const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
-+{
-+      return SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, conn, name, acltype, theacl);
-+}
-+
-+static int ads_sys_acl_delete_def_file(HANDLE_PARAMETER connection_struct *conn, const char *path)
-+{
-+      return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle, conn, path);
-+}
-+
-+#ifdef        ADS_NEW_MODULE 
-+static ssize_t ads_getxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, void *value, size_t size)
-+{
-+        return SMB_VFS_NEXT_GETXATTR(handle, conn, path, name, value, size);
-+}
-+
-+static ssize_t ads_lgetxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, void *value, size_t
-+size)
-+{
-+        return SMB_VFS_NEXT_LGETXATTR(handle, conn, path, name, value, size);
-+}
-+
-+static ssize_t ads_fgetxattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, void *value, size_t size)
-+{
-+        return SMB_VFS_NEXT_FGETXATTR(handle, fsp, fd, name, value, size);
-+}
-+
-+static ssize_t ads_listxattr(vfs_handle_struct *handle, connection_struct *conn,const char *path, char *list, size_t size)
-+{
-+        return SMB_VFS_NEXT_LISTXATTR(handle, conn, path, list, size);
-+}
-+
-+static ssize_t ads_llistxattr(vfs_handle_struct *handle,struct connection_struct *conn,const char *path, char *list, size_t size)
-+{
-+        return SMB_VFS_NEXT_LLISTXATTR(handle, conn, path, list, size);
-+}
-+
-+static int ads_removexattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name)
-+{
-+        return SMB_VFS_NEXT_REMOVEXATTR(handle, conn, path, name);
-+}
-+
-+static int ads_lremovexattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name)
-+{
-+        return SMB_VFS_NEXT_LREMOVEXATTR(handle, conn, path, name);
-+}
-+
-+static int ads_fremovexattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name)
-+{
-+        return SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, fd, name);
-+}
-+
-+static int ads_setxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags)
-+{
-+        return SMB_VFS_NEXT_SETXATTR(handle, conn, path, name, value, size, flags);
-+}
-+
-+static int ads_lsetxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags)
-+{
-+        return SMB_VFS_NEXT_LSETXATTR(handle, conn, path, name, value, size, flags);
-+}
-+
-+static int ads_fsetxattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, const void *value, size_t size, int flags)
-+{
-+        return SMB_VFS_NEXT_FSETXATTR(handle, fsp, fd, name, value, size, flags);
-+}
-+
-+#endif
-+
-+/* ---------------------------------- 
-+ * enumerate 
-+*/
-+static ssize_t ads_listads(HANDLE_PARAMETER struct connection_struct *conn,const char *path, char *list, size_t size)
-+{
-+      char *ads_path = 0;
-+      char *main_path = 0;
-+      TALLOC_CTX *ctx;
-+      size_t     len, total = 0;
-+      SMB_STRUCT_STAT st;
-+      SMB_STRUCT_STAT *main_info = &st;
-+      
-+
-+      if (!list || !path) {
-+              /* aka we have ads functionnality */
-+              return 0;
-+      }
-+
-+      DEBUG(3,("ads: ads_listads %s\n", path));
-+
-+      if (!(ctx = ADS_TALLOC_INIT("ads_listads")))
-+              return -1;
-+
-+      if (ads_build_paths(ctx, conn->origpath, path, &ads_path, &main_path, &main_info, 0) < 0) {
-+          talloc_destroy(ctx);
-+          return -1;
-+      }
-+
-+      /*
-+         if data stream
-+            for each stream
-+      */
-+      if (ads_path == main_path) {
-+          char *ads_base  = ads_dir(ctx, conn->origpath, path, isDir(main_info));
-+          struct dirent *dent = 0;
-+          DIR *dir = opendir(ads_base);
-+          
-+          /* XXX need to be root ? */
-+          if (dir) {
-+              
-+              while (NULL != (dent = readdir(dir))) {
-+                  if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
-+                      continue;
-+                  len = strlen(dent->d_name) +8 ;
-+                  total += len;
-+                  if (total >= size) {
-+                      talloc_destroy(ctx);
-+                      errno = ERANGE;
-+                      return -1;
-+                  }
-+                  snprintf (list, len, ":%s:$DATA", dent->d_name);
-+                  list += len;
-+              }
-+              closedir(dir);
-+          }
-+      }
-+
-+      talloc_destroy(ctx);
-+      return total;
-+}
-+
-+/* ------------------------------------
-+ * VFS operations structure */
-+
-+#ifndef SMB_VFS_OP
-+#define SMB_VFS_OP(x) ((void *) x)  
-+#endif
-+
-+static vfs_op_tuple ads_op_tuples[] = {
-+
-+      /* Disk operations */
-+
-+      {SMB_VFS_OP(ads_disk_free),                     SMB_VFS_OP_DISK_FREE,           SMB_VFS_LAYER_TRANSPARENT},
-+      
-+      /* Directory operations */
-+
-+      {SMB_VFS_OP(ads_opendir),                       SMB_VFS_OP_OPENDIR,             SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_readdir),                       SMB_VFS_OP_READDIR,             SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_mkdir),                         SMB_VFS_OP_MKDIR,               SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_rmdir),                         SMB_VFS_OP_RMDIR,               SMB_VFS_LAYER_TRANSPARENT},
-+
-+      /* File operations */
-+
-+      {SMB_VFS_OP(ads_open),                          SMB_VFS_OP_OPEN,                SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_rename),                        SMB_VFS_OP_RENAME,              SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_stat),                          SMB_VFS_OP_STAT,                SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_lstat),                         SMB_VFS_OP_LSTAT,               SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_unlink),                        SMB_VFS_OP_UNLINK,              SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_chmod),                         SMB_VFS_OP_CHMOD,               SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_chown),                         SMB_VFS_OP_CHOWN,               SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_chdir),                         SMB_VFS_OP_CHDIR,               SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_utime),                         SMB_VFS_OP_UTIME,               SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_symlink),                       SMB_VFS_OP_SYMLINK,             SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_readlink),                      SMB_VFS_OP_READLINK,            SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_link),                          SMB_VFS_OP_LINK,                SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_mknod),                         SMB_VFS_OP_MKNOD,               SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_realpath),                      SMB_VFS_OP_REALPATH,            SMB_VFS_LAYER_TRANSPARENT},
-+
-+      /* NT File ACL operations */
-+
-+      {SMB_VFS_OP(ads_set_nt_acl),                    SMB_VFS_OP_SET_NT_ACL,          SMB_VFS_LAYER_TRANSPARENT},
-+
-+      /* POSIX ACL operations */
-+
-+      {SMB_VFS_OP(ads_chmod_acl),                     SMB_VFS_OP_CHMOD_ACL,           SMB_VFS_LAYER_TRANSPARENT},
-+
-+      {SMB_VFS_OP(ads_sys_acl_get_file),              SMB_VFS_OP_SYS_ACL_GET_FILE,            SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_sys_acl_set_file),              SMB_VFS_OP_SYS_ACL_SET_FILE,            SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_sys_acl_delete_def_file),       SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE,     SMB_VFS_LAYER_TRANSPARENT},
-+#ifdef        ADS_NEW_MODULE
-+      /* EA operations. */
-+      {SMB_VFS_OP(ads_getxattr),                      SMB_VFS_OP_GETXATTR,                    SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_lgetxattr),                     SMB_VFS_OP_LGETXATTR,                   SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_fgetxattr),                     SMB_VFS_OP_FGETXATTR,                   SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_listxattr),                     SMB_VFS_OP_LISTXATTR,                   SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_llistxattr),                    SMB_VFS_OP_LLISTXATTR,                  SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_removexattr),                   SMB_VFS_OP_REMOVEXATTR,                 SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_lremovexattr),                  SMB_VFS_OP_LREMOVEXATTR,                SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_fremovexattr),                  SMB_VFS_OP_FREMOVEXATTR,                SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_setxattr),                      SMB_VFS_OP_SETXATTR,                    SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_lsetxattr),                     SMB_VFS_OP_LSETXATTR,                   SMB_VFS_LAYER_TRANSPARENT},
-+      {SMB_VFS_OP(ads_fsetxattr),                     SMB_VFS_OP_FSETXATTR,                   SMB_VFS_LAYER_TRANSPARENT},
-+#endif
-+      /* ADS operations */
-+      {SMB_VFS_OP(ads_listads),                       SMB_VFS_OP_LISTADS,                     SMB_VFS_LAYER_TRANSPARENT},
-+
-+      {NULL,                                          SMB_VFS_OP_NOOP,                        SMB_VFS_LAYER_NOOP}
-+};
-+
-+#ifdef ADS_NEW_MODULE
-+
-+#if 0
-+NTSTATUS vfs_ads_init(void)
-+{
-+        return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "ads", ads_op_tuples);
-+}
-+#endif
-+
-+NTSTATUS vfs_ads_init(void)
-+{
-+        DEBUG(3, ("ADS: vfs_ads_init\n"));
-+        return NT_STATUS_OK;
-+}
-+         
-+
-+NTSTATUS init_module(void)
-+{
-+      DEBUG(3, ("ADS: init_module\n" ));
-+        return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "ads", ads_op_tuples);
-+}
-+
-+#else
-+/* VFS initialisation function.  Return vfs_op_tuple array back to SAMBA. */
-+vfs_op_tuple *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops,struct smb_vfs_handle_struct *vfs_handle)
-+{
-+      *vfs_version = SMB_VFS_INTERFACE_VERSION;
-+        memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops));
-+                   
-+        ads_handle = vfs_handle;
-+        DEBUG(3, ("ADS: vfs module loaded\n"));
-+        return ads_op_tuples;
-+}
-+                                             
-+/* VFS finalization function. */
-+void vfs_done(connection_struct *conn)
-+{
-+      DEBUG(3, ("ADS: vfs module unloaded\n"));
-+}
-+
-+#endif
diff --git a/contrib/permtest/add_permtest.patch b/contrib/permtest/add_permtest.patch
deleted file mode 100644 (file)
index 6fdf38c..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-Index: configure.in
-===================================================================
-RCS file: /cvsroot/netatalk/netatalk/configure.in,v
-retrieving revision 1.205
-diff -u -w -b -r1.205 configure.in
---- configure.in       9 Sep 2006 04:30:01 -0000       1.205
-+++ configure.in       25 Jul 2008 13:48:05 -0000
-@@ -1053,6 +1053,7 @@
-       contrib/shell_utils/apple_rm
-       contrib/shell_utils/asip-status.pl
-       contrib/shell_utils/cleanappledouble.pl
-+      contrib/shell_utils/permtest.pl
-       contrib/timelord/Makefile
-       contrib/a2boot/Makefile
-       distrib/Makefile
-Index: contrib/shell_utils/Makefile.am
-===================================================================
-RCS file: /cvsroot/netatalk/netatalk/contrib/shell_utils/Makefile.am,v
-retrieving revision 1.16
-diff -u -w -b -r1.16 Makefile.am
---- contrib/shell_utils/Makefile.am    28 Apr 2005 20:49:36 -0000      1.16
-+++ contrib/shell_utils/Makefile.am    25 Jul 2008 13:48:05 -0000
-@@ -9,6 +9,9 @@
-       apple_cp apple_mv apple_rm      \
-       cleanappledouble.pl             \
-       asip-status.pl
-+EXTRASCRIPTS = \
-+      permtest.pl \
-+      permtest.cfg
- SUFFIXES = .tmpl .
-@@ -22,4 +25,4 @@
- bin_SCRIPTS = $(PERLSCRIPTS) $(GENERATED_FILES)
--EXTRA_DIST = $(PERLSCRIPTS) $(TEMPLATE_FILES)
-+EXTRA_DIST = $(PERLSCRIPTS) $(TEMPLATE_FILES) $(EXTRASCRIPTS)
diff --git a/contrib/permtest/permtest.cfg b/contrib/permtest/permtest.cfg
deleted file mode 100644 (file)
index 941d4e6..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-# Exactly follow this layout! Don't put extra white space in config lines !!
-# Order doesn't matter.
-
-# We use a ssh executed stat command to verify uid,gid and mode. Therefore ssh access with
-# PKI authentication must be setup and working!
-sshLogin = USER@HOST
-
-# self explaining
-mountAFPVolume = afp://USER:PASSWORD@HOST/VOLUME
-
-# These files will be created
-createFile = PATH_TO_FILE_ON_CLIENT
-
-# These files will be stat'ed. You can use different server-side paths here for files
-# created with "createFile" directive
-testFile = PATH_TO_FILE_ON_SERVER,user=USERNAME,group=GROUPNAME,mode=MODE
-
-# These dirs will be created
-createDir = PATH_TO_DIR_ON_CLIENT
-
-# These will be verified.
-testDir = PATH_TO_DIR_ON_CLIENT,user=USERNAME,group=GROUPNAME,mode=MODE
-
-# EOF. Leave this as a last line. We delibaretly chop in perl which might otherwise truncate
-# your last "testDir" definition.
\ No newline at end of file
diff --git a/contrib/permtest/permtest.pl.in b/contrib/permtest/permtest.pl.in
deleted file mode 100755 (executable)
index 87de19a..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-#!@PERL@ -w
-
-###########################################################################
-#
-# Characterization testing netatalks permission model
-#
-# (c) 2008 by Frank Lahm <franklahm@googlemail.com>
-#
-#   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.
-# 
-###########################################################################
-
-###########################################################################
-#
-# Usage:
-#
-#  "permtest.cfg" must be in your CWD. Must be run on a OS X host. Tested
-#  with 10.4.11. Uses Applescript through system("osascript ...") to mount
-#  AFP Volumes. Uses `ssh LOGIN@HOST stat FILE|DIR`. Therefor PKI
-#  authentication must be setup and working!
-#  See "permtest.cfg" for more details, it's pretty much self-explaining.
-# 
-###########################################################################
-
-use strict;
-
-my $DEBUG = 0;
-
-###########################################################################
-
-sub parseConfig;
-sub mountAPFVols;
-sub createTestFiles;
-sub createTestDirs;
-sub verifyTestFiles;
-sub verifyTestDirs;
-sub unmountAfp;
-
-my ($sshLogin, $sshResult, %sshStat, @AFPVols, @createFiles, @createDirs, @testFiles, @testDirs);
-my ($dir, $file, $user, $group, $perms, $mode, $cmd);
-
-parseConfig();
-mountAPFVols();
-createTestFiles();
-createTestDirs();
-print "\n";
-verifyTestFiles();
-verifyTestDirs();
-unmountAfp();
-
-exit 0;
-
-###########################################################################
-
-# parse config file
-sub parseConfig
-{
-       open CFG, "permtest.cfg" or die "Config file not found!";
-       while (<CFG>) {
-               chop;
-           if (/^#/) { next; };
-           if (/^sshLogin/) {
-                       $sshLogin = $_;
-                       $sshLogin =~ s/^sshLogin ?= ?// ;
-                       next;
-               }
-               if (/^mountAFPVolume/) {
-                       s/^mountAFPVolume ?= ?// ;
-                       print "Found AFP Volume Definition \"$_\"\n" if $DEBUG;
-                       push @AFPVols, $_;
-                       next;
-               }
-           if (/^createFile/) {
-                       s/^createFile ?= ?// ;
-                       push @createFiles, $_;
-                       next;
-               }
-               if (/^createDir/) {
-                       s/^createDir ?= ?// ;
-                       push @createDirs, $_;
-                       next;
-               }
-               if (/^testFile/) {
-                       push @testFiles, $_;
-                       next;
-               }
-               if (/^testDir/) {
-                       push @testDirs, $_;
-                       next;
-               }
-       }
-       close CFG;
-}
-
-# mount AFP Volumes
-sub mountAPFVols
-{
-       foreach (@AFPVols) {
-               print "Mounting AFP Volume \"$_\"\n";
-               $cmd = "osascript -e 'tell application \"Finder\"' -e 'mount volume \"$_\"' -e 'end tell' &> /dev/null";
-               print "Going to run the following Applescript:\n" . $cmd . "\n\n" if $DEBUG;
-               system($cmd);
-               if ($? != 0) { die "Error mounting \"$_\"\n"; }
-       }
-}
-
-# Create test files
-sub createTestFiles
-{
-       foreach (@createFiles) {
-               s/^createFile ?= ?// ;
-               system("rm \"$_\" &> /dev/null");
-               print "Creating file: \"$_\"\n";
-               system("touch \"$_\"");
-               if ($? != 0) { die "Error creating file \"$_\"\n"; }
-       }
-}
-
-# Create test dirs
-sub createTestDirs
-{
-       foreach (@createDirs) {
-               s/^createDir ?= ?// ;
-               system("rmdir \"$_\" &> /dev/null");
-               print "Creating dir: \"$_\"\n";
-               system("mkdir \"$_\"");
-               if ($? != 0) { die "Error creating dir \"$_\"\n"; }
-       }
-}
-
-# Verify files and dirs
-sub verifyTestFiles
-{
-       foreach (@testFiles) {
-               my @line = split(",");
-               foreach (@line) {
-                       if (/^testFile/) {
-                               $file = $_;
-                               $file =~ s/^testFile ?= ?//;
-                       }
-                       elsif (/^user/) {
-                               $user = $_;
-                               $user =~ s/^user ?= ?//;
-                       }
-                       elsif (/^group/) {
-                               $group = $_;
-                               $group =~ s/^group ?= ?//;
-                       }
-                       elsif (/^mode/) {
-                               $mode = $_;
-                               $mode =~ s/^mode ?= ?//;
-                       }
-               } # foreach (@elems)
-               print "File: $file, User: $user, Group: $group, Perms: $perms\n" if $DEBUG;
-       
-               $sshResult = `ssh $sshLogin stat -c \"user,%U,group,%G,mode,0%a\" \"$file\"`;
-               if ($? != 0) { die "Error stat'ing file \"$file\"\n"; }
-               chop $sshResult;
-               print "ssh stat $file gave us: $sshResult\n" if $DEBUG;
-       
-               %sshStat = split(",", $sshResult);
-               if ( ($sshStat{user} ne $user) or ($sshStat{group} ne $group) or ($sshStat{mode} ne $mode) ) {
-                       print "Creatin error for: \"$file\"!\nExpected:\t$user, $group, $mode.\nGot:\t\t$sshStat{user}, $sshStat{group}, $sshStat{mode}.\n\n";
-               }
-               system("rm \"$file\"");
-               if ($? != 0) { die "Couldn't delete \"$file\"\n"; }
-       }
-}
-
-sub verifyTestDirs
-{
-       foreach (@testDirs) {
-               my @line = split(",");
-               foreach (@line) {
-                       if (/^testDir/) {
-                               $dir = $_;
-                               $dir =~ s/^testDir ?= ?//;
-                       }
-                       elsif (/^user/) {
-                               $user = $_;
-                               $user =~ s/^user ?= ?//;
-                       }
-                       elsif (/^group/) {
-                               $group = $_;
-                               $group =~ s/^group ?= ?//;
-                       }
-                       elsif (/^mode/) {
-                               $mode = $_;
-                               $mode =~ s/^mode ?= ?//;
-                       }
-               } # foreach (@elems)
-               print "Dir: $dir, User: $user, Group: $group, Perms: $perms\n" if $DEBUG;
-       
-               $sshResult = `ssh $sshLogin stat -c \"user,%U,group,%G,mode,0%a\" \"$dir\"`;
-               if ($? != 0) { die "Error stat'ing file \"$dir\"\n"; }
-               chop $sshResult;
-               print "ssh stat $dir gave us: $sshResult\n" if $DEBUG;
-       
-               %sshStat = split(",", $sshResult);
-               if ( ($sshStat{user} ne $user) or ($sshStat{group} ne $group) or ($sshStat{mode} ne $mode) ) {
-                       print "Creatin error for: \"$dir\"!\nExpected:\t$user, $group, $mode.\nGot:\t\t$sshStat{user}, $sshStat{group}, $sshStat{mode}.\n\n";
-               }
-               system("rmdir \"$dir\""); if ($? != 0) { die "Couldn't delete \"$dir\"\n"; }
-       }
-}
-
-sub unmountAfp
-{
-       foreach (@AFPVols) {
-               print "Goint to eject Volume \"$_\"\n";
-               s#^(.*/)## ;
-               $cmd = "osascript -e 'tell application \"Finder\"' -e 'eject \"$_\"' -e 'end tell' &> /dev/null";
-               print "Going to run the following Applescript:\n" . $cmd . "\n\n" if $DEBUG;
-               system($cmd);
-       }
-}
index bb2f1496f10b47705e87f11d169f5343ca0ca203..c8cefe1e8ef8c8d0520b3c673e79474f43db1601 100644 (file)
@@ -6,7 +6,6 @@ GENERATED_FILES = lp2pap.sh
 TEMPLATE_FILES = lp2pap.sh.tmpl
 PERLSCRIPTS = \
        afpd-mtab.pl \
-       apple_cp apple_mv apple_rm  \
        asip-status.pl \
        apple_dump
 
diff --git a/contrib/shell_utils/apple_cp.in b/contrib/shell_utils/apple_cp.in
deleted file mode 100755 (executable)
index b8a75ab..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-#!@PERL@
-# 
-# $Id: apple_cp.in,v 1.1 2002-01-17 05:59:25 srittau Exp $
-
-$USAGE = <<USAGE;
-Usage: $0 filename1 filename2
-       $0 filename ...  directory
-Do an apple copy, copying the resource fork as well
-USAGE
-
-die $USAGE if @ARGV < 2;
-
-@from = @ARGV; pop(@from);
-$to = $ARGV[-1];
-
-if (-f $to && @from > 1) { die $USAGE; }
-
-foreach $from (@from) {
-    if (!-f $from) {   
-       print STDERR "file $from does not exist\n";
-       die $USAGE;
-    }
-    
-    if (!-d $to && @from >1) {
-       print STDERR "directory $to does not exist\nCan't copy multiple files into one file.\n";
-       die $USAGE;
-    }
-    
-    $cmd = "cp '$from' '$to'";
-    system $cmd || die "error executing $cmd";
-    
-    ($from_dir, $from_file) = split_dir_file($from);
-
-    if (-d $to) {
-       if (!-d "$to/.AppleDouble") {
-           mkdir("$to/.AppleDouble", 0777);
-       }       
-       $cmd = "cp '$from_dir/.AppleDouble/$from_file' '$to/.AppleDouble/$from_file'";
-    } else {
-       ($to_dir, $to_file) = split_dir_file($to);
-       if (!-d "$to_dir/.AppleDouble") {
-           mkdir("$to_dir/.AppleDouble", 0777);
-       }       
-       $cmd = "cp '$from_dir/.AppleDouble/$from_file' '$to_dir/.AppleDouble/$to_file'";
-    }
-
-    system $cmd || die "error executing $cmd";
-}
-
-# split a file path into a directory and file name.
-sub split_dir_file {
-    my $path = shift;
-
-    @path_elems = split(/\//, $path);
-
-    my $file = pop(@path_elems);
-    my $dir;
-    if (!@path_elems) {
-       $dir = '.';
-    } else {
-       $dir = join('/', @path_elems);
-    }
-
-    $dir, $file;
-}
-
-
diff --git a/contrib/shell_utils/apple_mv.in b/contrib/shell_utils/apple_mv.in
deleted file mode 100755 (executable)
index 79cf579..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-#!@PERL@
-# 
-# $Id: apple_mv.in,v 1.1 2002-01-17 05:59:25 srittau Exp $
-
-$USAGE = <<USAGE;
-Usage: $0 filename1 filename2
-       $0 filename ...  directory
-Do an apple move, moving the resource fork as well
-USAGE
-
-@from = @ARGV; pop(@from);
-$to = $ARGV[-1];
-
-if (-f $to && @from > 1) { die $USAGE; }
-
-foreach $from (@from) {
-    if (!-f $from) {   
-       print STDERR "file $from does not exist\n";
-       die $USAGE;
-    }
-
-    if (!-d $to && @from >1) {
-       print STDERR "directory $to does not exist\nCan't move multiple files into one file.\n";
-       die $USAGE;
-    }
-    
-    $from = escape_bad_chars($from);
-    $to = escape_bad_chars($to);
-    $cmd = "mv $from $to";
-    system $cmd || die "error executing $cmd";
-    
-    ($from_dir, $from_file) = split_dir_file($from);
-
-    if (-d $to) {
-       if (!-d "$to/.AppleDouble") {
-           mkdir("$to/.AppleDouble", 0777);
-       }
-       $cmd = "mv $from_dir/.AppleDouble/$from_file $to/.AppleDouble/$from_file";
-    } else {
-       ($to_dir, $to_file) = split_dir_file($to);
-
-       if (!-d $to_dir) {
-           print STDERR "directory $to does not exist\n";
-           die $USAGE;
-       }
-    
-       if (!-d "$to_dir/.AppleDouble") {
-           mkdir("$to_dir/.AppleDouble", 0777);
-       }
-       $cmd = "mv $from_dir/.AppleDouble/$from_file $to_dir/.AppleDouble/$to_file";
-    }
-
-    system $cmd || die "error executing $cmd";
-}
-
-sub escape_bad_chars {
-    my($file) = @_;
-    $file=~s/([^a-zA-Z0-9.-_])/\\$1/;
-    return $file;
-}
-
-# split a file path into a directory and file name.
-sub split_dir_file {
-    my $path = shift;
-
-    @path_elems = split(/\//, $path);
-
-    my $file = pop(@path_elems);
-    my $dir;
-    if (!@path_elems) {
-       $dir = '.';
-    } else {
-       $dir = join('/', @path_elems);
-    }
-
-    $dir, $file;
-}
-
-
diff --git a/contrib/shell_utils/apple_rm.in b/contrib/shell_utils/apple_rm.in
deleted file mode 100755 (executable)
index 6595937..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#!@PERL@
-# 
-# $Id: apple_rm.in,v 1.1 2002-01-17 05:59:25 srittau Exp $
-
-$USAGE = <<USAGE;
-Usage: $0 filename ...
-Do an apple remove, remove the resource fork as well
-USAGE
-
-die $USAGE if @ARGV < 1;
-
-foreach $path (@ARGV) {
-    if (!-f $path) {
-       print STDERR "file $path does not exist\n";
-       die $USAGE;
-    }
-
-    ($dir, $file) = &split_dir_file($path);
-
-    $cmd = "rm '$path'";
-    system $cmd || die "error executing $cmd";
-    
-    $cmd = "rm '$dir/.AppleDouble/$file'";
-    system $cmd || die "error executing $cmd";
-}
-
-# split a file path into a directory and file name.
-sub split_dir_file {
-    my $path = shift;
-
-    @path_elems = split(/\//, $path);
-
-    my $file = pop(@path_elems);
-    my $dir;
-    if (!@path_elems) {
-       $dir = '.';
-    } else {
-       $dir = join('/', @path_elems);
-    }
-
-    $dir, $file;
-}
index 226ec9e9d7d94eb76c942f635fad5871010fcfcd..ccd0332edd6c4ccf7094a71d2a1e08f8f743a9e4 100755 (executable)
@@ -7,38 +7,45 @@
 # author: James W. Abendschan  <jwa@jammed.com>
 # license: GPL - http://www.gnu.org/copyleft/gpl.html
 # url: http://www.jammed.com/~jwa/hacks/security/asip/
-# date: 7 May 1997 (v1.0)
+# Date: 7 May 1997 (v1.0) - original version
 # see also: 
 #   - http://developer.apple.com/techpubs/macos8/NetworkCommSvcs/AppleShare/
 #   - http://www2.opendoor.com/asip/   (excellent Mac sharing / security site)
 #
 # todo: log in as guest & get a list of shares
 #
-# $Id: asip-status.pl.in,v 1.2 2005-04-28 20:49:36 bfernhomberg Exp $
+
+#
+# This edition is a part of netatalk.
 #
 
 use strict;
 use IO::Socket;                        # sucks because Timeout doesn't
 
 my ($arg);
+my ($hostport);
 my ($host);
+my ($port);
 
 while ($arg = shift @ARGV)
 {
        $main::show_icon = 1 if ($arg eq "-i");
        $main::debug = 1 if ($arg eq "-d");
        $main::hexdump = 1 if ($arg eq "-x");
-       $host = $arg if ($arg !~ /^-/);
+       $hostport = $arg if ($arg !~ /^-/);
 }
 
-if ($host eq "")
+if ($hostport eq "")
 {
-       print "usage: $0 hostname [-i show icon] [-d debug] [-x hex dump]\n";
+       print "usage: $0 hostname[:port] [-i show icon] [-d debug] [-x hex dump]\n";
        exit(-1);
 }
 
+($host, $port) = split(/\:/, $hostport);
+$port = "548" if ($port eq "");
+
 my ($packet) = build_packet();
-my ($code) = sendpacket($host, 548, $packet);
+my ($code) = sendpacket($host, $port, $packet);
 exit $code;
 
 
@@ -205,6 +212,7 @@ sub parse_FPGetSrvrInfo()
 
        my ($icon_offset) = unpack("n", @packet[6] . @packet[7]);
        print "Volume Icon & Mask offset: $icon_offset\n" if ($main::debug);
+       print "Volume Icon & Mask: exist\n" if ($icon_offset);
 
        my ($flags) = unpack("n", @packet[8] . @packet[9]);
        my (@flags) = parse_afp_flags($flags);
@@ -269,7 +277,7 @@ sub parse_FPGetSrvrInfo()
                }
        }
 
-       draw_icon($icon_offset, @packet) if ($main::show_icon);
+       draw_icon($icon_offset, @packet) if ($main::show_icon && $icon_offset);
 
        return $allow_guest;
 }
@@ -391,6 +399,7 @@ sub parse_afp_flags
        push (@flags, "SupportsReconnect") if ($flags & (1<<7));
        push (@flags, "SupportsOpenDirectory") if ($flags & (1<<8));
        push (@flags, "SupportsUTF8Servername") if ($flags & (1<<9));
+       push (@flags, "SupportsUUIDs") if ($flags & (1<<10));
        push (@flags, "SupportsSuperClient") if ($flags & (1<<15));
 
        return @flags;
diff --git a/distrib/debian/README.Debian b/distrib/debian/README.Debian
deleted file mode 100644 (file)
index c51ccce..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-This is the pre-packaged Debian version of the Netatalk protocol suite.
-To find out more about netatalk, visit http://netatalk.sourceforge.net/
-
-This package was originally put together by Klee Dienes <klee@debian.org>
-and was later maintained by late Joel Klecker <espy@debian.org> and
-David Huggins-Daines <dhd@debian.org>. It was repackaged by its current
-maintainer Sebastian Rittau <srittau@debian.org>.
-
-                                 OpenSSL
-                                 =======
-
-Netatalk supports OpenSSL to provide a secure means for authentication.
-Unfortunately it's currently not possible for Debian to distribute
-Netatalk with SSL support enabled, since OpenSSL's license is incompatible
-with the GPL used by Netatalk.
-
-But it's possible to build Netatalk with SSL support locally:
-
-1. Make sure that the package libssl-dev is installed:
-
-  apt-get install libssl-dev
-
-2. Make sure all other build dependencies are fulfilled:
-
-  apt-get build-dep netatalk
-
-3. Download the Netatalk sources:
-
-  apt-get source netatalk
-
-4. Edit the rules file to enable SSL compilation:
-
-  cd netatalk-1.5pre8 && \
-  cp debian/rules debian/rules.bak && \
-  sed -e 's/^#USE_SSL=yes/USE_SSL=yes/' debian/rules.bak >debian/rules
-
-  (You may need to substitute the directory name netatalk-1.5pre8 with the
-  proper one.)
-
-5. Build the package:
-
-  dpkg-buildpackage -rfakeroot
diff --git a/distrib/debian/changelog b/distrib/debian/changelog
deleted file mode 100644 (file)
index d194da8..0000000
+++ /dev/null
@@ -1,576 +0,0 @@
-netatalk (1.5pre8cvs-0) unstable; urgency=low
-
-  * Unofficial CVS release.
-  * Correct included docs, since all platform specific docs were merged.
-  * Upstream does now have manpages for apple_cp(1), apple_mv(1), and
-    apple_rm(1), written by Lance Levsen <l.levsen@printwest.com>.
-    Removed the links to undocumented(7).
-  * Don't supply --with-did=last anymore, since this is now the default.
-  * Added cnid_didname_verify(1) to list of undocumented binaries.
-
- -- Sebastian Rittau <srittau@debian.org>  Mon, 10 Dec 2001 13:46:14 +0100
-
-netatalk (1.5pre8-5) unstable; urgency=low
-
-  * Appletalk -> AppleTalk in package short descriptions. Thanks to Matt
-    Zimmerman and his spell checking effort for pointing this out.
-  * Really install README.Debian this time.
-  * Removed afppasswd and afppasswd(1) from Debian distribution, since
-    they are of no use when SSL support is not compiled in.
-
- -- Sebastian Rittau <srittau@debian.org>  Sun, 18 Nov 2001 15:13:53 +0100
-
-netatalk (1.5pre8-4) unstable; urgency=low
-
-  * Fixed uams_pam.so. (Closes: #118889)
-  * Explain why we don't link against OpenSSL in README.Debian.
-  * Modified debian/rules so that setting a variable called USE_SSL to
-    "yes" enables SSL support. This should ease the local compilation of
-    SSL-enabled netatalk packages.
-
- -- Sebastian Rittau <srittau@debian.org>  Sat, 10 Nov 2001 19:05:12 +0100
-
-netatalk (1.5pre8-3) unstable; urgency=low
-
-  * Corrected upstream version number (pre8 instead of pre7). This corrects
-    afpd -v and similar commands.
-  * Raised default number of allowed afpd clients. Suggestion by Jonas
-    Smedegaard.
-  * Small logcheck fix by Jonas.
-  * Removed ATALK_BACKGROUND description from netatalk.conf(5).
-  * Removed obsolete --with-config-dir configure option.
-
- -- Sebastian Rittau <srittau@debian.org>  Sat, 27 Oct 2001 15:36:30 +0200
-
-netatalk (1.5pre8-2) unstable; urgency=low
-
-  * Work around the fact that upstream includes sym-links to mkinstalldirs and
-    missing instead of verbatim copies. We do that by including our own copies
-    in debian and copy them before running the build. (Closes: #114915)
-
- -- Sebastian Rittau <srittau@debian.org>  Wed, 10 Oct 2001 14:03:34 +0200
-
-netatalk (1.5pre8-1) unstable; urgency=low
-
-  * New upstream version, containing most Debian patches.
-  * Added a patch to configure.in that fixes PAM detection and compilation.
-
- -- Sebastian Rittau <srittau@debian.org>  Sun,  7 Oct 2001 12:46:15 +0200
-
-netatalk (1.5pre7-5) unstable; urgency=low
-
-  * More patches by Jonas Smedegaard <dr@jones.dk>:
-    + 001_logcheck_fix_typo_and_optimize...
-      Logcheck fixes and improvements. (Closes: #114448)
-    + 005_visible_home_dir_in_config_(again!)
-      Name user home directories "Home Directory" by default to make them
-      appear in the MacOS chooser. (Patch had already been applied in
-      1.5pre7-2, but had been lost since.)
-    + Jonas made more patches, which I haven't applied yet, but either
-      committed upstream or sent upstream for discussion.
-      
- -- Sebastian Rittau <srittau@debian.org>  Thu,  4 Oct 2001 22:31:50 +0200
-
-netatalk (1.5pre7-4) unstable; urgency=low
-
-  * Fixed Build-Dependencies. (pam-cracklib -> cracklib2-dev) (Closes: #113356)
-  * Restored symlinks in /usr/lib/atalk/filters and other directories.
-    (Closes: #113746)
-  * Patches by Jonas Smedegaard <dr@jones.dk>:
-    + 002_correctly_calculate_perl_depends
-    + 003_remove_cap_line_from_logcheck
-      Small logcheck change.
-    + 004_add_misc_logcheck_lines
-      Another logcheck change.
-    + 011_strip_pam_paths
-      Not applied, but patched config/netatalk.pamd to strip /lib/security
-      from its path.
-
- -- Sebastian Rittau <srittau@debian.org>  Mon,  1 Oct 2001 08:30:17 +0200
-
-netatalk (1.5pre7-3) unstable; urgency=low
-
-  * Fixed a stupid typo I made in the new init script.
-  * Put add_netatalk_printer and netatalkshorternamelinks.pl in the
-    examples directory instead of /usr/bin. Suggestion from Jonas
-    Smedegaard.
-
- -- Sebastian Rittau <srittau@debian.org>  Sun, 23 Sep 2001 19:08:43 +0200
-
-netatalk (1.5pre7-2) unstable; urgency=low
-
-  * Integrated a lot of patches by Jonas Smedegaard <dr@jones.dk>:
-    + 001_etc2ps paths
-      Correct paths in etc2ps and suggest tetex-base.
-    + 005_visible_home_dir_in_config
-      Name user home directories "Home Directory" by default to make them
-      appear in the MacOS chooser.
-    + 007_logcheck
-      Support for the logcheck log file checking package.
-    + 011_avoid_symlinks_and_force_using_autoconf_2.50
-      Partly applied: Patch configure.in so that the use of autoconf 2.50
-      is forced. (Debian autoconf hack workaround.)
-    + 012_netatalk.conf
-      Improved init script. Also, make use of netatalk.conf again.
-      I patched the patch so that netatalk.conf is placed in /etc/default.
-    + 015_recommend_lsof_(for_macusers)_and_suggest_quota
-      Recommend lsof and suggest quota.
-    + 021_enable_ssl_and_cracklib_and_correct_pam_paths
-      Partly applied: Enable cracklib support.
-  * Fixed paths in add_netatalk_printer.
-  * Removed lp2pap.sh since it's of no use on Debian systems.
-  * Removed test_parse_mtab and afpd-mtab.pl because we are not using
-    the mtab DID scheme.
-  * Comparison operator is '=', not '==' in the 'test' command. Fixed
-    my patch.
-  * Removed netatalk.conf.5 as well, since we don't install netatalk.conf
-    anymore.
-  * Removed superfluous file /etc/netatalk/netatalk.pamd.
-  * Moved all *.la and *.a files to netatalk-dev. Added appropriate
-    conflicts and replaces.
-  * debian/rules: Do not copy files to package build directories instead of
-    removing them afterwards.
-
- -- Sebastian Rittau <srittau@debian.org>  Sun, 23 Sep 2001 14:04:06 +0200
-
-netatalk (1.5pre7-1) unstable; urgency=medium
-
-  * New upstream version. Most patches were applied upstream.
-  * This release uses libtool for UAM stuff. Also, the correct flag
-    for dynamic linking is supplied, so the problems with unresolved
-    symbols should be gone now. (Closes: #95399)
-  * Non-DSFG free code was removed. Copyright notice was changed accordingly.
-  * Use ./configure --sysconfdir instead of --with-config-dir.
-  * Upstream package does now install PAM file in the correct directory.
-    Removed rule, correcting this from Debian rules file.
-  * Added man pages for netatalk-config(1) and timelord(8). (Upstream
-    does now also include a man page for timeout(1), but since we're not
-    distributing it anymore, we don't care.)
-  * Some doc files were removed, others were added.
-  * Use debhelper compatibility level 3 and performed general packaging
-    cleanups at the same time.
-  * Standards-Version 3.5.6.0. No changes needed.
-  * Netatalk is now GPL'ed. Added a note stating that to copyright.
-    Also, we can't link against libssl anymore. Removed SSL stuff.
-    I had to patch configure.in to do that.
-  * Removed emacs stuff from changelog.
-  * Applied a patch to getiface.c for a problem that could lead to
-    segfaults. Thanks to Kai Henningsen <kaih@khms.westfalen.de>
-    for actually being affected by this bug, and - more importantly -
-    finding the problem. (Closes: #109310)
-
- -- Sebastian Rittau <srittau@debian.org>  Thu, 30 Aug 2001 02:02:17 +0200
-
-netatalk (1.5pre6-7) unstable; urgency=low
-
-  * Cleaned up CFLAGS handling in ./configure call.
-  * Updated config.{sub,guess} again, just to make sure ...
-  * Depend on the timeout package from tct. Also, don't distribute
-    /usr/bin/timeout and remove the timeout(1) link to undocumented(7).
-    Make preparations to remove the proper timeout(1) man page that will
-    get distributed with netatalk 1.5pre7.
-
- -- Sebastian Rittau <srittau@debian.org>  Sun, 19 Aug 2001 18:05:55 +0200
-
-netatalk (1.5pre6-6) unstable; urgency=medium
-
-  * ./configure --with-did=last
-    This should fix errors with MacOS X.
-  * Fixed typo in add_netatalk_printer. (Closes: #104192)
-  * Removed /etc/netatalk/netatalk.conf, since it's not used by Debian's
-    init script. (Closes: #103539)
-  * Disabled pam_guest module by default. (Closes: #106637)
-
- -- Sebastian Rittau <srittau@debian.org>  Sat, 28 Jul 2001 14:49:15 +0200
-
-netatalk (1.5pre6-5) unstable; urgency=low
-
-  * Removed --without-ssl option from ./configure invocation. Not
-    that it had any effect before.
-  * Updated config.{sub,guess} (manually for now). I will switch to
-    dh_autotools if and when this is available. (Closes: #102861)
-
- -- Sebastian Rittau <srittau@debian.org>  Fri,  6 Jul 2001 00:46:18 +0200
-
-netatalk (1.5pre6-4) unstable; urgency=low
-
-  * Changed section of netatalk-dev to non-US, too.
-  * Make netatalk-dev depend on netatalk.
-
- -- Sebastian Rittau <srittau@debian.org>  Tue, 19 Jun 2001 01:40:07 +0200
-
-netatalk (1.5pre6-3) unstable; urgency=low
-
-  * Thanks to my former sponsor Michael 'grisu' Bramer for his efforts.
-  * Changed maintainer address to <srittau@debian.org>.
-  * Moved to section non-US and link against libssl. Changed Build-Depends
-    accordingly.
-  * Link against libdb3 instead of libdb2. Changed Build-Depends
-    accordingly.
-  * Sources were not obtained from CVS, and are available by HTTP.
-  * Removed patch to contrib/Makefile.* to enable compilation of timelord.
-    Instead, use configure option --with-timelord.
-  * Added symlinks to megatron. Use patch from upstream CVS. (Closes: #95944)
-  * Clean up patch for etc/psf/Makefile.am.
-  * Added DEB_BUILD_OPTIONS handling. (Closes: #99705)
-  * Added links to undocumented(7) from binheader(1) and nadheader(1).
-  * Standards-Version: 3.5.5.0.
-
- -- Sebastian Rittau <srittau@debian.org>  Sun, 17 Jun 2001 15:50:13 +0200
-
-netatalk (1.5pre6-2) unstable; urgency=low
-
-  * This version will hopefully clean up the version mess, I created.
-  * Conforms to standards-version 3.5.3.0 (no changes needed).
-  * Link cleanappledouble.pl(1) to undocumented(7).
-  * Removed all hand-crafted {pre,post}{inst,rm} files.
-  * Give files in /etc/netatalk/nls a mode of 0644, instead of 0755. Fixes
-    lintian warnings.
-  * Build-Depends on libdb2-dev do exist since -1. (Closes: #92774)
-  * Distribute missing pagecount.ps. (Closes: #95117)
-  * Compile timelord.
-  * Use --enable-fhs instead of --with-fhs. Should fix some paths.
-  * Compile with shadow support. (Closes: #95186)
-  * Use the pam_unix.so module instead of pam_pwdb.so in /etc/pam.d/netatalk.
-
- -- Sebastian Rittau <srittau@jroger.in-berlin.de>  Tue,  1 May 2001 03:38:57 +0200
-
-netatalk (1.5pre6-1) unstable; urgency=low
-
-  * New upstream release.
-  * Re-added changes made in 1.4b2+asun2.1.3-8.
-  * Added --prefix=/usr to ./configure options.
-
- -- Sebastian Rittau <srittau@jroger.in-berlin.de>  Fri, 13 Apr 2001 00:27:47 +0200
-
-netatalk (1.5pre5-3) unstable; urgency=low
-
-  * Re-added changes made in 1.4b2+asun2.1.3-8.
-
- -- Sebastian Rittau <srittau@jroger.in-berlin.de>  Fri,  6 Apr 2001 23:44:47 +0200
-
-netatalk (1.5pre5-2) unstable; urgency=low
-
-  * Added copyright of University of Newcastle upon Tyne to debian/copyright.
-  * Removed patches/uams_dhx_pam.c.patch as it was applied upstream.
-  * Some documentation files were moved into the doc subdirectory.
-  * Added more documentation files.
-  * Added some temporary build fixes.
-
- -- Sebastian Rittau <srittau@jroger.in-berlin.de>  Wed,  8 Mar 2001 00:03:30 +0100
-
-netatalk (1.5pre5-1) unstable; urgency=low
-
-  * New upstream version.
-
- -- Sebastian Rittau <srittau@jroger.in-berlin.de>  Fri, 23 Feb 2001 21:07:18 +0100
-
-netatalk (1.5pre4-1) unstable; urgency=low
-
-  * New upstream version.
-  * Some reorganisations to allow building directly from CVS.
-  * Debian packaging is now included in upstream CVS.
-  * Modified debian/copyright to include CVS instructions.
-  * Call ./configure with --with-fhs and removed --with-uams-path option.
-  * Removed patches/paths.h.patch as this is now supported by --with-fhs.
-  * Removed various build patches now included upstream.
-  * Use dh_installman from debhelper v3. Updated build dependencies
-    accordingly.
-  * Removed comment about Debian specific changes from debian/copyright.
-  * Build with libssl support. (Closes: #48871)
-  * Added libssl096-dev to Build-Depends.
-  * Ship FAQ in /usr/share/doc/netatalk
-
- -- Sebastian Rittau <srittau@jroger.in-berlin.de>  Thu, 22 Feb 2001 20:44:41 +0100
-
-netatalk (1.5pre3-1) unstable; urgency=low
-
-  * New upstream version from netatalk.sourceforge.net.
-    (Closes: #69232, #78781)
-  * Repackaged using debhelper.
-  * Conforms to policy version 3.5.1.0.
-  * Removed some Debian specific patches integrated upstream.
-  * Updated debian/copyright.
-  * Changed priority from optional to extra.
-
- -- Sebastian Rittau <srittau@jroger.in-berlin.de>  Thu, 22 Feb 2001 10:18:07 +0100
-
-netatalk (1.4b2+asun2.1.3-8) unstable; urgency=low
-
-  * Added libdb2-dev to build-depends. (Closes: #92774)
-  * Complies with Debian policy version 3.5.2.0.
-  * Added netatalk homepage and current maintainer to debian/copyright.
-
- -- Sebastian Rittau <srittau@jroger.in-berlin.de>  Tue,  3 Apr 2001 23:59:38 +0200
-
-netatalk (1.4b2+asun2.1.3-7) unstable; urgency=low
-
-  * New maintainer. (Closes: #82386)
-  * Fixed a build problem.
-  * Strip .note and .comment sections from /usr/lib/atalk/psa.
-  * Added debhelper as build-dependency.
-  * Complies with Debian policy version 3.2.1.0.
-
- -- Sebastian Rittau <srittau@jroger.in-berlin.de>  Sun, 21 Jan 2001 15:49:11 +0100
-
-netatalk (1.4b2+asun2.1.3-6) unstable; urgency=low
-
-  * The "looks like I picked the wrong week to quit sniffing glue" release.
-  * Update the maintainer name in the control file.
-  * Move psa and etc2ps to /usr/lib/atalk, as they are not user binaries
-    (this also shuts lintian up).
-
- -- David Huggins-Daines <dhd@debian.org>  Fri, 14 Jan 2000 21:04:24 -0500
-
-netatalk (1.4b2+asun2.1.3-5) unstable; urgency=low
-
-  * New maintainer.
-  * Compensate for stupid new 'install -s' behaviour. (closes:Bug#51423)
-  * Fix psf(8) manpage. (closes:Bug#30839)
-  * Updated Standards-Version.
-  * Fixed symlinks to be relative, as per lintian's warnings.
-  * Added /usr/doc symlinks in the postinst/prerm.
-
- -- David Huggins-Daines <dhd@debian.org>  Wed, 22 Dec 1999 20:24:26 -0500
-
-netatalk (1.4b2+asun2.1.3-4) unstable; urgency=low
-
-  * Fix init script to always kill papd even if ENABLE_PAP=no (closes:Bug#48783).
-
- -- Joel Klecker <espy@debian.org>  Sun, 31 Oct 1999 07:43:29 -0800
-
-netatalk (1.4b2+asun2.1.3-3) unstable; urgency=low
-
-  * Remove libatalk1 and libatalk1-dev (I think it is a mistake to "fork" a
-    shared version of a library in Debian, if the library is static upstream
-    then upstream isn't gonna be careful with the ABI).
-  * Create netatalk-dev.
-  * netatalk.init: Use $() instead of ``.
-    Use /bin/hostname explicitly.
-    s/daemons/Daemons/g.
-    Remove module fiddling (closes:Bug#44767,#43319).
-  * Remove "glibc 2.1 fix" it's no longer needed.
-  * Compile with sendfile support.
-  * Use /usr/share/doc.
-  * Cleanup bashisms in debian/rules.
-
- -- Joel Klecker <espy@debian.org>  Sat, 23 Oct 1999 20:59:24 -0700
-
-netatalk (1.4b2+asun2.1.3-2) unstable; urgency=low
-
-  * (netatalk): Make /etc/netatalk/afpd.conf a conffile (closes:Bug#37628).
-
- -- Joel Klecker <espy@debian.org>  Thu, 13 May 1999 10:54:37 -0700
-
-netatalk (1.4b2+asun2.1.3-1) unstable; urgency=low
-
-  * New upstream release (closes:Bug#33982).
-  * Correct paths in psf.8 (closes:Bug#30839).
-  * There is now a different way to control CRLF translation on a
-    per-volume basis upstream so I have removed the patch that
-    provides the -e option to afpd.
-  * (netatalk): Depend on libpam-modules.
-  * Put man pages in /usr/share/man.
-
- -- Joel Klecker <espy@debian.org>  Tue, 30 Mar 1999 12:17:36 -0800
-
-netatalk (1.4b2+asun2.1.1-2) frozen unstable; urgency=low
-
-  * Incorporated glibc 2.1 fixes from Christian Meder.
-  * Remove explicit add-log-mailing-address from debian/changelog.
-
- -- Joel Klecker <espy@debian.org>  Fri, 15 Jan 1999 07:28:11 -0800
-
-netatalk (1.4b2+asun2.1.1-1.1) frozen unstable; urgency=low
-
-  * non maintainer, sparc only upload
-  * fix #includes for glibc2.1
-
- -- Christian Meder <meder@isr.uni-stuttgart.de>  Mon,  4 Jan 1999 12:37:13 +0100
-
-netatalk (1.4b2+asun2.1.1-1) frozen unstable; urgency=low
-
-  * New upstream bugfix release.
-  * Recompile against libc6 2.0.7u-7 to get rid of versioned
-    libc6 dependency.
-
- -- Joel Klecker <espy@debian.org>  Thu,  3 Dec 1998 07:45:42 -0800
-
-netatalk (1.4b2+asun2.1.0-5) frozen unstable; urgency=high
-
-  * [libatalk/atp/atp_rsel.c] Minor change for libnatali compatibility
-    (closes:Bug#30092).
-  * Rebuild with libc6 2.0.7u-6 for i386.
-
- -- Joel Klecker <espy@debian.org>  Fri, 27 Nov 1998 22:58:11 -0800
-
-netatalk (1.4b2+asun2.1.0-4) frozen unstable; urgency=low
-
-  * binary-arch target now depends on pre-binary (closes:Bug#29508)
-
- -- Joel Klecker <espy@debian.org>  Tue, 17 Nov 1998 04:46:50 -0800
-
-netatalk (1.4b2+asun2.1.0-3) frozen unstable; urgency=low
-
-  * Now installs /usr/lib/atalk/pagecount.ps (closes:Bug#29323)
-
- -- Joel Klecker <espy@debian.org>  Thu, 12 Nov 1998 00:30:53 -0800
-
-netatalk (1.4b2+asun2.1.0-2) frozen unstable; urgency=low
-
-  * Should build from freshly unpacked source now (Bug#28810)
-
- -- Joel Klecker <espy@debian.org>  Sun,  1 Nov 1998 19:34:52 -0800
-
-netatalk (1.4b2+asun2.1.0-1) unstable; urgency=low
-  
-  * New upstream release.
-  * Incorporate megatron patch from Rob Browning (Bug#25598).
-  * Don't install /usr/include/netatalk on glibc 2.1 architectures.
-  * Fix paths in /etc/pam.d/netatalk file.
-
- -- Joel Klecker <espy@debian.org>  Thu, 29 Oct 1998 23:54:13 -0800
-
-netatalk (1.4b2+asun2.0a18.2-1) frozen unstable; urgency=low
-
-  * New "upstream" release.
-  * This does add new features, however, it also fixes at
-    least one nasty bug (Bug#13973).
-  * Applied patch which adds a command-line option to disable
-    CR/LF translation (thanks to Davide Welton and Jon Nelson).
-    (Note to release manager: this patch is applied so this
-     package has the exact functionality of netatalk-asun)
-  * Renamed libatalk-dev to libatalk1-dev.
-  * Symlinked /usr/man/man1/nbpunrgstr.1.gz to /usr/man/man1/nbprgstr.1.gz
-    to keep lintian happy.
-  * Changed the "lock directory" to /var/run and the names of the "lock files" to <foo>.pid,
-    since what the source calls locks are really the same as the .pid files other daemons
-    put in /var/run.
-  * This package provides all the functionality of netatalk-asun, and
-    it will replace netatalk-asun in the distribution.
-
- -- Joel Klecker <jk@espy.org>  Tue, 12 May 1998 19:31:54 -0700
-
-netatalk (1.4b2-5) frozen unstable; urgency=low
-
-  * New Maintainer (I can finally close bugs
-    I fixed in previous releases ;).
-  * Changed library package names again.
-  * Upgraded to Debian Policy 2.4.0.0.
-  * Moved conffiles to /etc/netatalk.
-  * Fixes almost all lintian warnings/errors.
-  * Cleaned up changelog.
-
- -- Joel Klecker <jk@espy.org>  Sun, 22 Mar 1998 21:50:00 -0800
-
-netatalk (1.4b2-4.5) unstable; urgency=low
-
-  * Non-maintainer release (again :>)
-  * Made libatalk14g-dev conflict with libc5-dev to fix overlap
-    (Bug:#17848)
-
- -- Joel Klecker <jk@espy.org>  Thu, 5 Feb 1998 20:42:51 -0800
-
-netatalk (1.4b2-4.4) unstable; urgency=low
-
-  * Yet Another non-maintainer release.
-  * Added patch to fix "dancing icon" problems with Macs running Mac OS 8.
-  * Changed comment in /etc/AppleVolumes.default (Bug:#15279)
-  * Implemented variable for "server name" in init script
-    (as suggested in Bug:#12024)
-  * Added a kluge to /etc/init.d/netatalk to remove kernel appletalk
-    module (if there is one) at stop and reinsert it at start, this
-    is needed or else netatalk will not start once stopped (Bug:#12142,11349)
-
- -- Joel Klecker <jk@espy.org>  Fri, 30 Jan 1998 07:50:00 -0800
-
-netatalk (1.4b2-4.3) unstable; urgency=low
-
-  * Non-maintainer release.
-  * Fixed dependencies.
-
- -- Joel Klecker <jk@espy.org>  Thu, 8 Jan 1998 16:14:17 -0800
-
-netatalk (1.4b2-4.2) unstable; urgency=low
-  
-  * Non-maintainer release.
-  * Changed library package names.
-
- -- Joel Klecker <jk@espy.org>  Wed, 7 Jan 1998 00:00:00 -0800
-
-netatalk (1.4b2-4.1) unstable; urgency=low
-
-  * Non-maintainer libc6 compile.
-
- -- Joel Klecker <jk@espy.org>  Tue, 6 Jan 1998 00:00:00 -0800
-
-netatalk (1.4b2-4) unstable; urgency=low
-
-  * Recompiled against newer PAM libraries.
-  * Added /etc/pam.d/samba.
-
- -- Klee Dienes <klee@debian.org>  Sat, 8 Mar 1997 01:17:09 -0500
-
-netatalk (1.4b2-3) unstable; urgency=low
-
-  * Added PAM support.
-  * Split into libatalk, libatalk-dev, and netatalk.
-  * Added patch from Randy Gobbel <gobbel@cogsci.ucsd.edu> to allow case
-    translation to be specified at config-time rather than compile time.
-    Note that configuration files that make use of this feature may not
-    work with other releases of netatalk, and that this feature may be
-    removed in the future if UMich rejects the patch or implements it
-    differently.
-  * Startup messages now conform to 'Standard for Console Messages' (fixes
-    #5399).
-  * No longer creates new subdirectories (to appease dpkg-buildpackage).
-
- -- Klee Dienes <klee@debian.org>  Wed, 26 Feb 1997 21:02:02 -0500
-
-netatalk (1.4b2-2) unstable; urgency=low
-
-  * Resend_request made external for libnatali.
-  * Added shared libraries.
-  * Next revision will split into libatalk, libatalk-dev, and netatalk.
-
- -- Klee Dienes <klee@debian.org>  Fri, 24 Jan 1997 22:37:22 -0500
-
-netatalk (1.4b2-1) unstable; urgency=low
-
-  * Updated to upstream version 1.4b2.
-  * Added preliminary PAM support (currently disabled).
-  * Made /etc/init.d/netatalk a conffile.
-  * Changed /etc/init.d/netatalk to complete only once appletalk services
-    are running.  Configurating an Appletalk interface can take many (> 15)
-    seconds, so the previous version would fork a process to configure the
-    interface and then start up the other Appletalk services.  Although
-    possibly controversial, this change is necessary so that packages like
-    ppr can be ensured that netatalk will be started before they run
-    without undue complication.
-
- -- Klee Dienes <klee@debian.org>  Sat, 2 Nov 1996 19:42:04 -0700
-
-netatalk (1.4b1-1) unstable; urgency=low
-
-  * Updated to new upstream version.
-  * Updated to new packaging format.
-
- -- Klee Dienes <klee@debian.org>  Wed, 2 Oct 1996 10:18:14 -0700
-
-netatalk (1.3.3-3);
-
-  * Fixed location of include files.
-
- -- Klee Dienes <klee@mit.edu>  Mon Jan  8 10:46:52 MST 1996
-
-netatalk (1.3.3-2);
-
-  * Fixed bug in postrm script.
-
- -- Klee Dienes <klee@mit.edu>  Thu Dec 21 08:22:24 MST 1995
-
-netatalk (1.3.3-1);
-
-  * Initial Release.
-
- -- Klee Dienes <klee@mit.edu>  Wed Dec 13 22:58:31 MST 1995
diff --git a/distrib/debian/control b/distrib/debian/control
deleted file mode 100644 (file)
index fa3de94..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-Source: netatalk
-Section: non-US
-Priority: extra
-Maintainer: Sebastian Rittau <srittau@debian.org>
-Standards-Version: 3.5.6.0
-Build-Depends: debhelper (>= 3.0.0), libdb3-dev, libwrap0-dev, libpam0g-dev, cracklib2-dev
-
-Package: netatalk
-Architecture: any
-Depends: netbase, timeout, libpam-modules, ${shlibs:Depends}, ${perl:Depends}
-Recommends: lsof, libpam-cracklib
-Suggests: tetex-base, quota
-Conflicts: netatalk-asun, libatalk14g, libatalk1
-Replaces: netatalk-asun, libatalk14, libatalk1
-Description: AppleTalk user binaries
- Netatalk is an implementation of the AppleTalk Protocol Suite for
- BSD-derived systems.  The current release contains support for
- EtherTalk Phase I and II, DDP, RTMP, NBP, ZIP, AEP, ATP, PAP, ASP, and
- AFP.
-
-Package: netatalk-dev
-Architecture: any
-Depends: netatalk (>= 1.5)
-Conflicts: netatalk (<< 1.5pre7-2), libatalk14g-dev, libatalk14-dev, netatalk-asun, libatalk1-dev
-Replaces: netatalk (<< 1.5pre7-2), libatalk14g-dev, libatalk14-dev, netatalk-asun, libatalk1-dev
-Description: AppleTalk library and development files
- Netatalk is an implementation of the AppleTalk Protocol Suite for
- BSD-derived systems.  The current release contains support for
- EtherTalk Phase I and II, DDP, RTMP, NBP, ZIP, AEP, ATP, PAP, ASP, and
- AFP.
-
diff --git a/distrib/debian/copyright b/distrib/debian/copyright
deleted file mode 100644 (file)
index c284410..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-This is the Debian GNU/Linux prepackaged version of netatalk.
-
-This package was originally put together by Joel 'epsy' Klecker.
-Current maintainer is Sebastian Rittau <srittau@debian.org>.
-
-The sources were obtained from CVS: cvs.netatalk.sourceforge.net.
-See the Netatalk homepage at <http://netatalk.sourceforge.net/> for
-anonymous CVS instructions.
-
-The following copyrights/licenses apply to this software:
-
-The GNU General Public License (GPL). See /usr/share/common-licenses/GPL
-for more information.
-
-This software includes software developed by the University of Michigan.
-
-Copyright (c) 1990,1996 Regents of The University of Michigan.
-All Rights Reserved.
-
-    Permission to use, copy, modify, and distribute this software and
-    its documentation for any purpose and without fee is hereby granted,
-    provided that the above copyright notice appears in all copies and
-    that both that copyright notice and this permission notice appear
-    in supporting documentation, and that the name of The University
-    of Michigan not be used in advertising or publicity pertaining to
-    distribution of the software without specific, written prior
-    permission. This software is supplied as is without expressed or
-    implied warranties of any kind.
-
-This product includes software developed by the University of
-California, Berkeley and its contributors.
-
-Solaris code is encumbered by the following:
-    Copyright (C) 1996 by Sun Microsystems Computer Co.
-    Permission to use, copy, modify, and distribute this software and
-    its documentation for any purpose and without fee is hereby
-    granted, provided that the above copyright notice appear in all
-    copies and that both that copyright notice and this permission
-    notice appear in supporting documentation.  This software is
-    provided "as is" without express or implied warranty.
-
-Modifications for Appleshare IP and other files copyrighted by Adrian
-Sun are under the following copyright:
-
-    Copyright (c) 1997,1998,1999,2000 Adrian Sun (asun@cobalt.com)
-    All Rights Reserved.
-
-    Permission to use, copy, modify, and distribute this software and
-    its documentation for any purpose and without fee is hereby granted,
-    provided that the above copyright notice appears in all copies and
-    that both that copyright notice and this permission notice appear
-    in supporting documentation. This software is supplied as is
-    without expressed or implied warranties of any kind.
-
-Research Systems Unix Group
-The University of Michigan
-c/o Wesley Craig
-535 W. William Street
-Ann Arbor, Michigan
-+1-313-764-2278
-netatalk@umich.edu
diff --git a/distrib/debian/cvs2deb.sh b/distrib/debian/cvs2deb.sh
deleted file mode 100755 (executable)
index 699131f..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/bin/sh
-
-# Execute this script from the main netatalk source directory.
-
-set -e
-
-debiandir=distrib/debian
-
-if test ! -d libatalk; then
-  echo "Not in netatalk directory"
-  exit 1
-fi
-
-VERSION=`cat VERSION`
-DEBVERSION="${VERSION}cvs"
-DISTDIR="netatalk-$VERSION"
-DISTTGZ="netatalk_$DEBVERSION.orig.tar.gz"
-
-if test ! -f INSTALL; then
-  if test -e INSTALL; then
-    echo "INSTALL is in the way, please move it away"
-    exit 1
-  fi
-  touch INSTALL
-fi
-
-if test ! -x configure; then
-  ./autogen.sh
-fi
-
-if test ! -e Makefile; then
-  if test -x config.status; then
-    ./config.status
-  else
-    ./configure
-  fi
-fi
-
-make dist
-
-mv "netatalk-$VERSION.tar.gz" "$DISTTGZ"
-rm -rf "$DISTDIR" || true
-tar xzf "$DISTTGZ"
-
-for FILE in `find $debiandir/patches/*.patch`; do
-  patch --dir="$DISTDIR" --strip=1 <$FILE
-done
-
-cp -a "$debiandir" "$DISTDIR"
-rm -r "$DISTDIR/debian/CVS"
-rm -r "$DISTDIR/debian/patches"
-rm -r "$DISTDIR/debian/split-init"
-rm    "$DISTDIR/debian/cvs2deb.sh"
-
-cd $DISTDIR
-
-touch INSTALL
-
-automake
-
-dpkg-buildpackage -rfakeroot
-
-cd ..
-rm -r "$DISTDIR"
-rm INSTALL
diff --git a/distrib/debian/logcheck/ignore.d.server b/distrib/debian/logcheck/ignore.d.server
deleted file mode 100644 (file)
index bf7b562..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-afpd\[.*\]: ((dhx|dhx2) )?login: [[:alnum:]]+
-afpd\[.*\]: (server_child\[[[:digit:]]+\] [[:digit:]]+ )?(done|exited 1)
-afpd\[.*\]: [\.[:alnum:]]+ read, [\.[:alnum:]]+ written
-afpd\[.*\]: .*: Broken pipe
-afpd\[.*\]: .*: Connection reset by peer
-afpd\[.*\]: .*: Connection timed out
-afpd\[.*\]: .*: No route to host
-afpd\[.*\]: .*: No such file or directory
-afpd\[.*\]: .*: Permission denied
-afpd\[.*\]: .*: child timed out
-afpd\[.*\]: ASIP session:[[:digit:]]+\([[:digit:]]+\) from [\.:[:digit:]]+\([[:digit:]]+\)
-afpd\[.*\]: Connection terminated
-afpd\[.*\]: afp_openfork: ad_open: File Exists
-afpd\[.*\]: asp_alrm: [[:digit:]]+ timed out
-afpd\[.*\]: login [[:alnum:]]+ \(uid [[:digit:]]+, gid [[:digit:]]+\)
-afpd\[.*\]: login noauth
-afpd\[.*\]: logout [[:alnum:]]+
-afpd\[.*\]: registering [[:alnum:]]+ \(uid [[:digit:]]+\) on [\.[:digit:]]+ as /.+/net[\.[:digit:]]+node[[:digit:]]+
-afpd\[.*\]: session from [\.:[:digit:]]+ on [\.:[:digit:]]+
-afpd\[.*\]: uams_dhx_pam.c :PAM: PAM (Auth OK!|Success -- Success)
-afpd\[.*\]: uams_dhx2_pam.c :PAM: PAM (Auth OK!|Success -- Success)
-afpd\[.*\]: using codepage directory: /etc/netatalk/nls/maccode\.[\.[:alnum:]-]+
-atalkd\[.*\]: .*: Network is unreachable
-atalkd\[.*\]: zip gnireply from [\.[:digit:]]+ \(.* [[:digit:]]\)
-atalkd\[.*\]: zip ignoring gnireply
-papd\[.*\]: child [[:digit:]]+ for ".+" from [\.[:digit:]]+
-papd\[.*\]: child [[:digit:]]+ done
diff --git a/distrib/debian/logcheck/violations.ignore.d b/distrib/debian/logcheck/violations.ignore.d
deleted file mode 100644 (file)
index 98638a7..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-afpd\[.*\]: afp_die: asp_shutdown: Connection timed out
-afpd\[.*\]: afp_getsrvrparms: stat /.+/: Permission denied
-afpd\[.*\]: dsi_stream_read\([[:digit:]]+\): Permission denied
-afpd\[.*\]: getforkparms: (ad_refresh|of_find): Permission denied
diff --git a/distrib/debian/netatalk-dev.docs b/distrib/debian/netatalk-dev.docs
deleted file mode 100644 (file)
index 976f224..0000000
+++ /dev/null
@@ -1 +0,0 @@
-doc/DEVELOPER
diff --git a/distrib/debian/netatalk-dev.files b/distrib/debian/netatalk-dev.files
deleted file mode 100644 (file)
index a4f3f2b..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-usr/bin/netatalk-config
-usr/include/atalk/adouble.h
-usr/include/atalk/aep.h
-usr/include/atalk/afp.h
-usr/include/atalk/asp.h
-usr/include/atalk/atp.h
-usr/include/atalk/cnid.h
-usr/include/atalk/compat.h
-usr/include/atalk/ddp.h
-usr/include/atalk/dsi.h
-usr/include/atalk/nbp.h
-usr/include/atalk/netddp.h
-usr/include/atalk/pap.h
-usr/include/atalk/paths.h
-usr/include/atalk/rtmp.h
-usr/include/atalk/server_child.h
-usr/include/atalk/uam.h
-usr/include/atalk/util.h
-usr/include/atalk/zip.h
-usr/include/netatalk/aarp.c
-usr/include/netatalk/aarp.h
-usr/include/netatalk/at_control.c
-usr/include/netatalk/at_proto.c
-usr/include/netatalk/at_var.h
-usr/include/netatalk/ddp.h
-usr/include/netatalk/ddp_input.c
-usr/include/netatalk/ddp_output.c
-usr/include/netatalk/ddp_usrreq.c
-usr/include/netatalk/ddp_var.h
-usr/include/netatalk/endian.h
-usr/include/netatalk/phase2.h
-usr/lib/libatalk.a
-usr/lib/libatalk.la
-usr/lib/netatalk/uams_*.la
-usr/lib/netatalk/uams_*.a
-usr/share/aclocal
-usr/share/man/man1/netatalk-config.1
-usr/share/man/man3
-usr/share/man/man4
diff --git a/distrib/debian/netatalk.dirs b/distrib/debian/netatalk.dirs
deleted file mode 100644 (file)
index b8ce978..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-etc/default
-etc/logcheck/ignore.d.server
-etc/logcheck/ignore.d.workstation
-etc/logcheck/violations.ignore.d
diff --git a/distrib/debian/netatalk.docs b/distrib/debian/netatalk.docs
deleted file mode 100644 (file)
index c8a19c9..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-BUGS
-CHANGES
-CONTRIBUTORS
-TODO
-doc/CONFIGURE
-doc/FAQ
-doc/README.hidden-items
-doc/README.platforms
diff --git a/distrib/debian/netatalk.examples b/distrib/debian/netatalk.examples
deleted file mode 100644 (file)
index 43e3fc4..0000000
+++ /dev/null
@@ -1 +0,0 @@
-contrib/printing/add_netatalk_printer
diff --git a/distrib/debian/netatalk.files b/distrib/debian/netatalk.files
deleted file mode 100644 (file)
index 4fddf68..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-etc/netatalk/AppleVolumes.default
-etc/netatalk/AppleVolumes.system
-etc/netatalk/afpd.conf
-etc/netatalk/atalkd.conf
-etc/netatalk/papd.conf
-etc/netatalk/nls
-etc/pam.d/netatalk
-usr/bin/achfile
-usr/bin/adv1tov2
-usr/bin/aecho
-usr/bin/afile
-usr/bin/apple_cp
-usr/bin/apple_mv
-usr/bin/apple_rm
-usr/bin/cleanappledouble.pl
-usr/bin/getzones
-usr/bin/macusers
-usr/bin/makecode
-usr/bin/megatron
-usr/bin/pap
-usr/bin/papstatus
-usr/bin/parsecode
-usr/bin/psorder
-usr/bin/nbplkup
-usr/bin/nbprgstr
-usr/bin/nbpunrgstr
-usr/bin/nu
-usr/bin/unbin
-usr/bin/unhex
-usr/bin/unsingle
-usr/bin/hqx2bin
-usr/bin/single2bin
-usr/bin/macbinary
-usr/bin/binheader
-usr/bin/nadheader
-usr/lib/atalk/filters/etc2ps.sh
-usr/lib/atalk/filters/ofpap
-usr/lib/atalk/filters/ifpap
-usr/lib/atalk/filters/tfpap
-usr/lib/atalk/filters/ifpaprev
-usr/lib/atalk/filters/tfpaprev
-usr/lib/atalk/filters/ofwpap
-usr/lib/atalk/filters/ifwpap
-usr/lib/atalk/filters/tfwpap
-usr/lib/atalk/filters/ifwpaprev
-usr/lib/atalk/filters/tfwpaprev
-usr/lib/atalk/filters/ofmpap
-usr/lib/atalk/filters/ifmpap
-usr/lib/atalk/filters/tfmpap
-usr/lib/atalk/filters/ifmpaprev
-usr/lib/atalk/filters/tfmpaprev
-usr/lib/atalk/filters/ofwmpap
-usr/lib/atalk/filters/ifwmpap
-usr/lib/atalk/filters/tfwmpap
-usr/lib/atalk/filters/ifwmpaprev
-usr/lib/atalk/filters/tfwmpaprev
-usr/lib/netatalk/uams_*.so
-usr/sbin/afpd
-usr/sbin/atalkd
-usr/sbin/papd
-usr/sbin/psf
-usr/sbin/psa
-usr/sbin/timelord
-usr/share/man/man1/aecho.1
-usr/share/man/man1/afile.1
-usr/share/man/man1/getzones.1
-usr/share/man/man1/megatron.1
-usr/share/man/man1/nbp.1
-usr/share/man/man1/pap.1
-usr/share/man/man1/achfile.1
-usr/share/man/man1/acleandir.1
-usr/share/man/man1/hqx2bin.1
-usr/share/man/man1/macbinary.1
-usr/share/man/man1/nbplkup.1
-usr/share/man/man1/nbprgstr.1
-usr/share/man/man1/nbpunrgstr.1
-usr/share/man/man1/papstatus.1
-usr/share/man/man1/psorder.1
-usr/share/man/man1/single2bin.1
-usr/share/man/man1/unbin.1
-usr/share/man/man1/unhex.1
-usr/share/man/man1/unsingle.1
-usr/share/man/man5/afpd.conf.5
-usr/share/man/man5/atalkd.conf.5
-usr/share/man/man5/papd.conf.5
-usr/share/man/man5/AppleVolumes.default.5
-usr/share/man/man8/pap.8
-usr/share/man/man8/papd.8
-usr/share/man/man8/papstatus.8
-usr/share/man/man8/psf.8
-usr/share/man/man8/timelord.8
-usr/share/man/man8/afpd.8
-usr/share/man/man8/atalkd.8
-usr/share/netatalk
diff --git a/distrib/debian/netatalk.init b/distrib/debian/netatalk.init
deleted file mode 100644 (file)
index fe5e1c7..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/bin/sh
-
-test -x /usr/sbin/atalkd || exit 0
-
-# Set defaults. Please change these options in /etc/default/netatalk.
-AFPD_UAMLIST="-U uams_clrtxt.so,uams_randnum.so"
-AFPD_GUEST=nobody
-AFPD_MAX_CLIENTS=20
-ATALK_NAME=`/bin/hostname --short`
-
-# Read in netatalk configuration.
-if [ -f /etc/default/netatalk ]; then
-  . /etc/default/netatalk
-fi
-
-OPTIONS_AFP="$AFPD_UAMLIST -g $AFPD_GUEST -c $AFPD_MAX_CLIENTS -n $ATALK_NAME"
-
-case "$1" in
-    start)
-       if [ "$ATALKD_RUN" = "yes" ]; then
-               # Quickly probe for appletalk if it was supposed to be loaded
-               if grep '^appletalk$' /etc/modules; then
-                       /sbin/modprobe appletalk || echo "[could not load appletalk module]"
-               fi
-
-               echo -n "Starting AppleTalk Daemons (this will take a while):"
-               /usr/sbin/atalkd
-               echo -n " atalkd"
-
-               /usr/bin/nbprgstr -p 4 "$ATALK_NAME:Workstation"
-               /usr/bin/nbprgstr -p 4 "$ATALK_NAME:netatalk"
-       fi
-
-       if [ "$AFPD_RUN" = "yes" ]; then
-               /usr/sbin/afpd $OPTIONS_AFP
-               echo -n " afpd"
-       fi
-
-       if [ "$ATALKD_RUN" = "yes" -a "$PAPD_RUN" = "yes" ]; then
-               /usr/sbin/papd
-               echo -n " papd"
-       fi
-
-       if [ "$TIMELORD_RUN" = "yes" ]; then
-               /usr/sbin/timelord
-               echo -n " timelord"
-       fi
-
-       echo "."
-    ;;
-
-    stop)
-       echo -n "Stopping AppleTalk Daemons:"
-       echo -n " afpd"; \
-       start-stop-daemon --stop --quiet --oknodo --exec /usr/sbin/afpd
-
-       echo -n " papd"; \
-       start-stop-daemon --stop --quiet --oknodo --exec /usr/sbin/papd
-
-       echo -n " timelord"; \
-       start-stop-daemon --stop --quiet --oknodo --exec /usr/sbin/timelord
-
-       echo -n " atalkd"; \
-       start-stop-daemon --stop --quiet --oknodo --exec /usr/sbin/atalkd
-
-       echo "."
-    ;;
-    
-    restart)
-       $0 force-reload
-    ;;
-
-    force-reload)
-       echo -n "Restarting AppleTalk Daemons (this will take a while)"
-       /etc/init.d/netatalk stop > /dev/null 2>&1
-       echo -n "."
-       sleep 2
-       echo -n "."
-       if /etc/init.d/netatalk start > /dev/null 2>&1; then
-               echo "done."
-       fi
-    ;;
-  
-    *)
-       echo "Usage: /etc/init.d/netatalk {start|stop|restart|force-reload}" >&2
-       exit 1
-    ;;
-esac
-
diff --git a/distrib/debian/netatalk.links b/distrib/debian/netatalk.links
deleted file mode 100644 (file)
index cc12933..0000000
+++ /dev/null
@@ -1 +0,0 @@
-etc/logcheck/ignore.d.server/netatalk etc/logcheck/ignore.d.workstation/netatalk
diff --git a/distrib/debian/netatalk.undocumented b/distrib/debian/netatalk.undocumented
deleted file mode 100644 (file)
index eb6a7b8..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-binheader.1
-cleanappledouble.pl.1
-cnid_didname_verify.1
-macusers.1
-makecode.1
-nadheader.1
-nu.1
-parsecode.1
-psa.8
diff --git a/distrib/debian/patches/add_printer.patch b/distrib/debian/patches/add_printer.patch
deleted file mode 100644 (file)
index bbfb908..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
---- netatalk.cvs/contrib/printing/add_netatalk_printer
-+++ netatalk.debian/contrib/printing/add_netatalk_printer
-@@ -43,10 +43,10 @@
- # allow for the env NETATALKHOME to override the guessed one from above
--NETATALKHOME=${NETATALKHOME:-$RUNHOME}
-+NETATALKHOME=${NETATALKHOME:-/usr}
- export NETATALKHOME
--PATH=/bin:${PATH}:${NETATALKHOME}/bin:${NETATALKHOME}/etc:${NETATALKHOME}/etc/filters:/usr/lib:/usr/sbin
-+PATH=/bin:${PATH}:${NETATALKHOME}/bin:${NETATALKHOME}/etc:${NETATALKHOME}/lib/atalk/filters:/usr/lib:/usr/sbin
- if [ ! -x ${NETATALKHOME}/bin/pap ]; then
-        echo "OOPS:     I don't see ${NETATALKHOME}/bin/pap ,"
-@@ -66,7 +66,7 @@
- echo ""
- echo "Looking for LaserWriters in Zone ${ZONE} ..."
--$NETATALKHOME/bin/nbplkup ":LaserWriter@${ZONE}"
-+${NETATALKHOME}/bin/nbplkup ":LaserWriter@${ZONE}"
- echo ""
- echo "Enter AppleTalk printer name: \c"
-@@ -80,7 +80,7 @@
- echo "checking nbplkup ${DEST}:LaserWriter@${ZONE}"
- echo ""
--TestDEST=`$NETATALKHOME/bin/nbplkup "${DEST}:LaserWriter@${ZONE}"`
-+TestDEST=`${NETATALKHOME}/bin/nbplkup "${DEST}:LaserWriter@${ZONE}"`
- echo "${TestDEST}"
- echo ""
-@@ -237,7 +237,7 @@
- Printer types: Netatalk
- Printers: any
- Filter type: fast
--Command: ${NETATALKHOME}/etc/filters/ifpap 2>&1 > /dev/null
-+Command: ${NETATALKHOME}/lib/atalk/filters/ifpap 2>&1 > /dev/null
- EOF
-        chown lp:lp /etc/lp/fd/netatalk.fd
-        chmod 664   /etc/lp/fd/netatalk.fd
-@@ -257,7 +257,7 @@
- Printer types: Netatalk-R
- Printers: any
- Filter type: fast
--Command: "/usr/lib/lp/postscript/postreverse | ${NETATALKHOME}/etc/filters/ifpap 2>&1 >/dev/null"
-+Command: "/usr/lib/lp/postscript/postreverse | ${NETATALKHOME}/lib/atalk/filters/ifpap 2>&1 >/dev/null"
- EOF
-        chown lp:lp /etc/lp/fd/netatalk-r.fd
-        chmod 664   /etc/lp/fd/netatalk-r.fd
diff --git a/distrib/debian/patches/etc2ps.sh.patch b/distrib/debian/patches/etc2ps.sh.patch
deleted file mode 100644 (file)
index c9c76d7..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
---- netatalk.cvs/etc/psf/etc2ps.sh
-+++ netatalk.debian/etc/psf/etc2ps.sh
-@@ -9,11 +9,11 @@
- # tag in the case.
- #
--DVIPSPATH=/usr/local/tex/bin
--DVIPS=/usr/local/tex/bin/dvips
-+DVIPSPATH=/usr/bin
-+DVIPS=/usr/bin/dvips
- DVIPSARGS="-f -q"
--TROFF2PS=/usr/local/psroff/troff2/troff2ps
-+TROFF2PS=/usr/bin/troff2ps
- TROFF2PSARGS="-Z -O-.10"
- PATH=/usr/bin:$DVIPSPATH; export PATH
diff --git a/distrib/debian/patches/filterdir.patch b/distrib/debian/patches/filterdir.patch
deleted file mode 100644 (file)
index 65855d9..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
---- netatalk.cvs/etc/psf/Makefile.am
-+++ netatalk.debian/etc/psf/Makefile.am
-@@ -1,8 +1,11 @@
- # Makefile.am for etc/psf/
-+filterdir = $(libdir)/atalk/filters
-+
- sbin_PROGRAMS = psf psa
- pkgdata_DATA = pagecount.ps
-+filter_SCRIPTS = etc2ps.sh
- psf_SOURCES = psf.c
- psa_SOURCES = psa.c
-@@ -26,14 +29,16 @@
- # install sections for links
- #
-+# srittau: We do some dirty hard-coding for Debian to maintain compability.
- install-exec-local:
-+       $(mkinstalldirs) $(DESTDIR)$(filterdir)
-        @list='$(psf_LINKS)'; for l in $$list; do \
--               $(LN_S) -f psf $(DESTDIR)$(sbindir)/$$l;  \
-+               $(LN_S) -f ../../../sbin/psf $(DESTDIR)$(filterdir)/$$l;  \
-        done
- #
diff --git a/distrib/debian/patches/netatalk.conf.patch b/distrib/debian/patches/netatalk.conf.patch
deleted file mode 100644 (file)
index 5487084..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
---- netatalk.cvs/config/netatalk.conf
-+++ netatalk.debian/config/netatalk.conf
-@@ -6,7 +6,7 @@
- # NOTE: if you're zone has spaces in it, you're better off specifying
- #       it in afpd.conf
- #ATALK_ZONE=@zone
--ATALK_NAME=`echo ${HOSTNAME}|cut -d. -f1`
-+ATALK_NAME=`/bin/hostname --short`
- # specify this if you don't want guest, clrtxt, and dhx
- # available options: uams_guest.so, uams_clrtxt.so, uams_dhx.so, 
-@@ -21,6 +21,3 @@
- PAPD_RUN=yes
- AFPD_RUN=yes
- TIMELORD_RUN=no
--
--# Control whether the daemons are started in the background
--ATALK_BGROUND=no
diff --git a/distrib/debian/patches/netatalk.pamd.patch b/distrib/debian/patches/netatalk.pamd.patch
deleted file mode 100644 (file)
index b1758d1..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
---- netatalk.cvs/config/netatalk.pamd
-+++ netatalk.debian/config/netatalk.pamd
-@@ -1,6 +1,6 @@
- #%PAM-1.0
--auth       required    /lib/security/pam_unix.so
--account    required    /lib/security/pam_unix.so 
--#password   required   /lib/security/pam_cracklib.so
--#password   required   /lib/security/pam_unix.so use_authtok
--session    required    /lib/security/pam_unix.so 
-+auth       required    pam_unix.so
-+account    required    pam_unix.so 
-+password   required    pam_cracklib.so
-+password   required    pam_unix.so use_authtok
-+session    required    pam_unix.so 
diff --git a/distrib/debian/patches/psf.8.patch b/distrib/debian/patches/psf.8.patch
deleted file mode 100644 (file)
index 3cc2ab8..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
---- netatalk.cvs/man/man8/psf.8.tmpl
-+++ netatalk.debian/man/man8/psf.8.tmpl
-@@ -90,13 +90,13 @@
- .RS
- .nf
- laser|lp|LaserWriter Plus on AppleTalk:\\
--    :sd=/usr/spool/lpd/laser:\\
--    :lp=/usr/spool/lpd/laser/null:\\
--    :lf=/var/adm/lpd-errs:pw#80:hl:\\
--    :of=:LIBDIR:/filters/ofpap:\\
--    :if=:LIBDIR:/filters/ifpaprev:\\
--    :tf=:LIBDIR:/filters/tfpaprev:\\
--    :df=:LIBDIR:/filters/dfpaprev:
-+    :sd=/var/spool/lpd/laser:\\
-+    :lp=/var/spool/lpd/laser/null:\\
-+    :lf=/var/log/lpd-errs:pw#80:hl:\\
-+    :of=:LIBDIR:/atalk/filters/ofpap:\\
-+    :if=:LIBDIR:/atalk/filters/ifpaprev:\\
-+    :tf=:LIBDIR:/atalk/filters/tfpaprev:\\
-+    :df=:LIBDIR:/atalk/filters/dfpaprev:
- .fi
- .RE
- .sp
diff --git a/distrib/debian/rules b/distrib/debian/rules
deleted file mode 100755 (executable)
index 824b41d..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-#! /usr/bin/make -f
-
-# Uncomment the following line to enable OpenSSL support. (If it's installed.)
-#USE_SSL=yes
-
-# Uncomment this to turn on verbose mode.
-#export DH_VERBOSE=1
-
-# This is the debhelper compatability version to use.
-export DH_COMPAT=3
-
-# support the DEB_BUILD_OPTIONS variable (partly stolen from gnome-utils)
-CFLAGS := -O2
-LDFLAGS :=
-ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS)))
-  CFLAGS += -g
-  LDFLAGS += -g
-endif
-export CFLAGS
-export LDFLAGS
-
-CONFIGURE_FLAGS = \
-       --with-shadow --enable-fhs --sysconfdir=/etc/netatalk           \
-       --with-tcp-wrappers --mandir=/usr/share/man --prefix=/usr       \
-       --enable-timelord --enable-overwrite                            \
-       --with-cracklib=/var/cache/cracklib/cracklib_dict
-ifneq "x$(USE_SSL)" "xyes"
-CONFIGURE_FLAGS += --without-ssl-dir
-endif
-
-configure: configure-stamp
-configure-stamp:
-       dh_testdir
-
-       ./configure $(CONFIGURE_FLAGS)
-
-       touch configure-stamp
-
-build: configure-stamp build-stamp
-build-stamp:
-       dh_testdir
-
-       $(MAKE)
-
-       touch build-stamp
-
-clean:
-       dh_testdir
-       dh_testroot
-       rm -f build-stamp configure-stamp
-
-       -$(MAKE) distclean
-
-       dh_clean
-
-install: build
-       dh_testdir
-       dh_testroot
-       dh_clean -k
-       dh_installdirs
-
-       $(MAKE) install DESTDIR=$(CURDIR)/debian/tmp
-
-       # Manually move a file that would get installed in the wrong place.
-       mv debian/tmp/etc/netatalk/netatalk.conf debian/netatalk/etc/default/netatalk
-
-       # Install logcheck files
-       install -m 644 debian/logcheck/ignore.d.server debian/netatalk/etc/logcheck/ignore.d.server/netatalk
-       install -m 644 debian/logcheck/violations.ignore.d debian/netatalk/etc/logcheck/violations.ignore.d/netatalk
-
-# Build architecture-independent files here.
-binary-indep: build install
-# We have nothing to do by default.
-
-binary-arch: build install
-       dh_testdir
-       dh_testroot
-       dh_movefiles
-
-       dh_installdocs
-       dh_installexamples
-       dh_installinit --update-rcd-params="defaults 50 50"
-       dh_installman
-       dh_undocumented
-       dh_installchangelogs ChangeLog
-       dh_link
-       dh_strip
-       dh_compress
-       dh_fixperms
-       dh_makeshlibs
-       dh_installdeb
-       dh_shlibdeps
-       dh_perl
-       dh_gencontrol
-       dh_md5sums
-       dh_builddeb
-
-binary: binary-indep binary-arch
-.PHONY: build clean binary-indep binary-arch binary install configure
index ef9ee0ddfaa9a6f2eca475ff8ae4cd171b328046..5ad0d37e4891e430f26160929a79af8324ee4975 100644 (file)
@@ -80,7 +80,7 @@ endif
 if USE_SUSE
 
 sysvdir        = /etc/init.d
-sysv_SCRIPTS = atalk
+sysv_SCRIPTS = netatalk
 
 $(sysv_SCRIPTS): rc.atalk.suse
        cp -f rc.atalk.suse $(sysv_SCRIPTS)
@@ -102,7 +102,7 @@ endif
 if USE_COBALT
 
 sysvdir        = /etc/rc.d/init.d
-sysv_SCRIPTS = atalk
+sysv_SCRIPTS = netatalk
 
 $(sysv_SCRIPTS): rc.atalk.cobalt
        cp -f rc.atalk.cobalt $(sysv_SCRIPTS)
@@ -123,7 +123,7 @@ endif
 if USE_TRU64
 
 sysvdir        = /etc/init.d
-sysv_SCRIPTS = atalk
+sysv_SCRIPTS = netatalk
 
 $(sysv_SCRIPTS): rc.atalk.tru64
        cp -f rc.atalk.tru64 $(sysv_SCRIPTS)
@@ -207,18 +207,18 @@ endif
 if USE_GENTOO
 
 sysvdir = /etc/init.d
-sysv_SCRIPTS = atalk
+sysv_SCRIPTS = netatalk
 
 $(sysv_SCRIPTS): rc.atalk.gentoo
        cp -f rc.atalk.gentoo $(sysv_SCRIPTS)
        chmod a+x $(sysv_SCRIPTS)
 
 install-data-hook:
-       -rc-update add $(sysv_SCRIPTS) default
+#      -rc-update add $(sysv_SCRIPTS) default
 
 uninstall-startup:
-       -rc-update del $(sysv_SCRIPTS) default
-       rm -f $(DESTDIR)$(sysvdir)/$(sysv_SCRIPTS)
+#      -rc-update del $(sysv_SCRIPTS) default
+#      rm -f $(DESTDIR)$(sysvdir)/$(sysv_SCRIPTS)
 
 endif
 
index 05010b7ba320b9b6f3cbfad4b1af6177f8287553..6a0f489a27db73014cf0661c683a1e3f98913f94 100755 (executable)
 ##     /usr/etc/modload -sym :ETCDIR:/netatalk.o;
 ##fi
 
+ATALK_NAME=`hostname|sed 's/\..*$//'`
+ATALK_UNIX_CHARSET='LOCALE'
+ATALK_MAC_CHARSET='MAC_ROMAN'
+
+CNID_METAD_RUN=yes
+AFPD_RUN=yes
+AFPD_MAX_CLIENTS=20
+AFPD_UAMLIST="-U uams_dhx.so,uams_dhx2.so"
+AFPD_GUEST=nobody
+CNID_CONFIG="-l log_note"
+
+ATALKD_RUN=no
+PAPD_RUN=no
+TIMELORD_RUN=no
+#A2BOOT_RUN=no
+ATALK_ZONE=
+#ATALK_BGROUND=no
+
 netatalk_conf=":ETCDIR:/netatalk.conf"
 
 [ -f ${netatalk_conf} ] && . ${netatalk_conf}
@@ -26,8 +44,8 @@ if [ -x :SBINDIR:/atalkd ]; then
 fi
 
 if [ -x :BINDIR:/nbprgstr ]; then
-       :BINDIR:/nbprgstr -p 4 `hostname|sed 's/\..*$//'`:Workstation
-       :BINDIR:/nbprgstr -p 4 `hostname|sed 's/\..*$//'`:netatalk
+       :BINDIR:/nbprgstr -p 4 ${ATALK_NAME}:Workstation
+       :BINDIR:/nbprgstr -p 4 ${ATALK_NAME}:netatalk
        echo -n ' nbprgstr'
 fi
 fi
@@ -37,12 +55,14 @@ if [ -x :SBINDIR:/papd -a X"${PAPD_RUN}" != X"no" ]; then
 fi
 
 if [ -x :SBINDIR:/cnid_metad -a X"${CNID_METAD_RUN}" != X"no" ]; then
-    :SBINDIR:/cnid_metad $CNID_CONFIG
-    echo -n ' cnid_metad'
+       :SBINDIR:/cnid_metad $CNID_CONFIG
+       echo -n ' cnid_metad'
 fi
 
 if [ -x :SBINDIR:/afpd -a X"${AFPD_RUN}" != X"no" ]; then
-       :SBINDIR:/afpd;         echo -n ' afpd'
+       :SBINDIR:/afpd ${AFPD_UAMLIST} -g ${AFPD_GUEST} \
+           -c ${AFPD_MAX_CLIENTS} -n ${ATALK_NAME}${ATALK_ZONE}
+       echo -n ' afpd'
 fi
 
 if [ -x :SBINDIR:/timelord -a X"${TIMELORD_RUN}" != X"no" ]; then
index 7fce712f635e17e5b1bce933f4ac8a11e34ebb34..a9fafd1320b8a2e4429b1295dc0bf830102212b9 100644 (file)
@@ -9,7 +9,6 @@
 #
 # netatalk      Netatalk 2.x initscript
 # Author:       Thomas Kaiser <Thomas.Kaiser@phg-online.de>
-# Version:      $Id: rc.atalk.debian.tmpl,v 1.6 2009-06-09 11:58:49 franklahm Exp $
 
 set -e
 
@@ -19,18 +18,29 @@ NAME=netatalk
 SCRIPTNAME=/etc/init.d/$NAME
 
 # Guard to prevent execution if netatalk was removed.
-test -x :SBINDIR:/atalkd || exit 0
+test -x :SBINDIR:/afpd || exit 0
 
 # Set defaults. Please change these options in /etc/default/netatalk
+ATALK_NAME=`/bin/hostname --short`
+ATALK_UNIX_CHARSET='LOCALE'
+ATALK_MAC_CHARSET='MAC_ROMAN'
+
+CNID_METAD_RUN=yes
+AFPD_RUN=yes
+AFPD_MAX_CLIENTS=50
 AFPD_UAMLIST="-U uams_dhx2.so"
 AFPD_GUEST=nobody
-AFPD_MAX_CLIENTS=50
+CNID_CONFIG="-l log_note"
+
+ATALKD_RUN=no
+PAPD_RUN=no
+TIMELORD_RUN=no
+#A2BOOT_RUN=no
 ATALK_ZONE=
-ATALK_NAME=`/bin/hostname --short`
 ATALK_BGROUND=no
-CNID_METAD_RUN=yes
-ATALK_MAC_CHARSET='MAC_ROMAN'
-ATALK_UNIX_CHARSET='LOCALE'
+
+# old /etc/default/netatalk expected hostname in $HOSTNAME by default
+HOSTNAME=`/bin/hostname`
 
 # Read in netatalk configuration.
 if [ -f /etc/default/netatalk ]; then
@@ -43,7 +53,7 @@ atalk_startup() {
 
        # Try to load the AppleTalk kernel module if it was intended.
     if grep -q '^appletalk$' /etc/modules; then
-        /sbin/modprobe appletalk || echo "[could not load appletalk module]"
+               /sbin/modprobe appletalk || echo "[could not load appletalk module]"
     fi
 
        # Start atalkd server.
@@ -83,7 +93,7 @@ atalk_startup() {
 
 case "$1" in
        start)
-               if test "x$ATALK_BGROUND" = "xyes"; then
+               if [ "x$ATALK_BGROUND" = "xyes" -a "x$ATALKD_RUN" = "xyes" ]; then
                        echo "Starting Netatalk services in the background."
                        atalk_startup >/dev/null &
                else
@@ -101,16 +111,20 @@ case "$1" in
                echo -n " cnid_metad"
                start-stop-daemon --stop --quiet --oknodo --exec :SBINDIR:/cnid_metad
        
-               echo -n " papd"
-               start-stop-daemon --stop --quiet --oknodo --exec :SBINDIR:/papd
+               if test -x :SBINDIR:/papd; then
+                    echo -n " papd"
+                   start-stop-daemon --stop --quiet --oknodo --exec :SBINDIR:/papd
+               fi
        
                if test -x :SBINDIR:/timelord; then
                     echo -n " timelord"
                    start-stop-daemon --stop --quiet --oknodo --exec :SBINDIR:/timelord
                fi
 
-               echo -n " atalkd"
-               start-stop-daemon --stop --quiet --oknodo --exec :SBINDIR:/atalkd
+               if test -x :SBINDIR:/atalkd; then
+                    echo -n " atalkd"
+                   start-stop-daemon --stop --quiet --oknodo --exec :SBINDIR:/atalkd
+               fi
        
                echo "."
        ;;
index c75ed74b9d0dbfbfabbffd396da62ca5cf2c69a2..5ed475c556acff5b1c9b4ed3a0c83c86d9c6f148 100644 (file)
@@ -5,6 +5,24 @@
 # its data structures must have time to stablize before running the
 # other processes.
 
+ATALK_NAME=`echo ${HOSTNAME}|cut -d. -f1`
+ATALK_UNIX_CHARSET='LOCALE'
+ATALK_MAC_CHARSET='MAC_ROMAN'
+
+CNID_METAD_RUN=yes
+AFPD_RUN=yes
+AFPD_MAX_CLIENTS=20
+AFPD_UAMLIST="-U uams_dhx.so,uams_dhx2.so"
+AFPD_GUEST=nobody
+CNID_CONFIG="-l log_note"
+
+ATALKD_RUN=no
+PAPD_RUN=no
+TIMELORD_RUN=no
+#A2BOOT_RUN=no
+ATALK_ZONE=
+ATALK_BGROUND=no
+
 depend() {
        need net
        use logger dns
@@ -37,7 +55,8 @@ atalk_startup () {
 
        if [ "${CNID_METAD_RUN}" = "yes" ] ; then
         ebegin "Starting cnid_metad"
-               start-stop-daemon --start --quiet --exec :SBINDIR:/cnid_metad $CNID_CONFIG
+               start-stop-daemon --start --quiet --exec :SBINDIR:/cnid_metad -- \
+                       ${CNID_CONFIG}
                eend $?
        fi
 
@@ -60,7 +79,7 @@ atalk_startup () {
 start () {
        . :ETCDIR:/netatalk.conf
 
-        if [ x"${ATALK_BGROUND}" = x"yes" ]; then
+        if [ x"${ATALK_BGROUND}" = x"yes" -a "${ATALKD_RUN}" != "no" ]; then
             echo "Starting netatalk in the background ... "
             atalk_startup >& /dev/null &
         else
index dfeed4cae5150eb2a2104fb797b5a1c0a331cc1a..79b613bd69b614afb1200af5c1f07c67946ddd52 100644 (file)
@@ -19,6 +19,25 @@ ATALK_SBIN=:SBINDIR:
 # Source networking configuration.
 . /etc/sysconfig/network
 
+# default
+ATALK_NAME=`echo ${HOSTNAME}|cut -d. -f1`
+ATALK_UNIX_CHARSET='LOCALE'
+ATALK_MAC_CHARSET='MAC_ROMAN'
+
+CNID_METAD_RUN=yes
+AFPD_RUN=yes
+AFPD_MAX_CLIENTS=20
+AFPD_UAMLIST="-U uams_dhx.so,uams_dhx2.so"
+AFPD_GUEST=nobody
+CNID_CONFIG="-l log_note"
+
+ATALKD_RUN=no
+PAPD_RUN=no
+TIMELORD_RUN=no
+A2BOOT_RUN=no
+ATALK_ZONE=
+ATALK_BGROUND=no
+
 # read in netatalk configuration
 if [ -f ${ATALK_CONF_DIR}/netatalk.conf ]; then
     . ${ATALK_CONF_DIR}/netatalk.conf
@@ -39,21 +58,16 @@ atalk_startup() {
          exit 1;
     fi
 
-    if [ ! -x ${ATALK_SBIN}/atalkd ]; then
-         # Quickly probe for appletalk and warn if we can't find it
-         #/sbin/modprobe appletalk || echo "[could not load appletalk module]"
-         # Check for IP Encapsulation support
-         #/sbin/modprobe ipddp || echo "[could not load IP encapsulation]"
-         echo "[${ATALK_SBIN}/atalkd not found. Check for permissions]";
-         exit 4;
-    fi
-
     if [ ! -f ${ATALK_CONF_DIR}/netatalk.conf ]; then
          echo "[${ATALK_CONF_DIR}/netatalk.conf not found]";
          exit 6;
     fi
 
-    if [ x"${ATALKD_RUN}" != x"no" ]; then 
+    if [ x"${ATALKD_RUN}" != x"no" -a -x ${ATALK_SBIN}/atalkd ]; then 
+         # Quickly probe for appletalk and warn if we can't find it
+         #/sbin/modprobe appletalk || echo "[could not load appletalk module]"
+         # Check for IP Encapsulation support
+         #/sbin/modprobe ipddp || echo "[could not load IP encapsulation]"
        echo -n "  Starting atalkd:"
        daemon ${ATALK_SBIN}/atalkd
        RETVAL_ATALKD=$?
@@ -125,7 +139,7 @@ atalk_startup() {
 case "$1" in
 'start')
        echo -n 'Starting Netatalk services: '
-       if [ x"${ATALK_BGROUND}" = x"yes" ]; then 
+       if [ x"${ATALK_BGROUND}" = x"yes" -a x"${ATALKD_RUN}" != x"no" ]; then 
            echo -n "(backgrounded)"
            atalk_startup >& /dev/null &
        else
index c9a0bec35039219b4841d3c0b5c3de90bc4d85f9..886591bcd8a98a7067d1f09967df84d3ea3393d8 100755 (executable)
@@ -30,6 +30,25 @@ test -f /etc/rc.status && . /etc/rc.status
 return=$rc_done
 }
 
+ATALK_NAME=`hostname|sed 's/\..*$//'`
+ATALK_UNIX_CHARSET='LOCALE'
+ATALK_MAC_CHARSET='MAC_ROMAN'
+
+CNID_METAD_RUN=yes
+AFPD_RUN=yes
+AFPD_MAX_CLIENTS=20
+AFPD_UAMLIST="-U uams_dhx.so,uams_dhx2.so"
+AFPD_GUEST=nobody
+CNID_CONFIG="-l log_note"
+
+ATALKD_RUN=no
+PAPD_RUN=no
+TIMELORD_RUN=no
+#A2BOOT_RUN=no
+ATALK_ZONE=
+ATALK_BGROUND=no
+
+
 . :ETCDIR:/netatalk.conf
 
 # startup code for everything
@@ -39,8 +58,8 @@ atalk_startup() {
        :SBINDIR:/atalkd
 
        if [ -x :BINDIR:/nbprgstr ]; then       
-           :BINDIR:/nbprgstr -p 4 `hostname|sed 's/\..*$//'`:Workstation
-           :BINDIR:/nbprgstr -p 4 `hostname|sed 's/\..*$//'`:netatalk
+           :BINDIR:/nbprgstr -p 4 ${ATALK_NAME}:Workstation
+           :BINDIR:/nbprgstr -p 4 ${ATALK_NAME}:netatalk
 
        fi      
 
@@ -74,7 +93,7 @@ atalk_startup() {
 
 case "$1" in
     start)
-       if [ x"${ATALK_BGROUND}" = x"yes" ]; then 
+       if [ x"${ATALK_BGROUND}" = x"yes" -a x"${ATALKD_RUN}" != x"no" ]; then 
            echo "Starting netatalk in the background ... "
            atalk_startup >& /dev/null &
        else
index f21c7ec9dff80981b5cae7dd1c6cbbf47f5838d7..85ec0748f5b6add6f72f3522d3b5dd42e41915b2 100755 (executable)
@@ -18,7 +18,26 @@ killproc() {
        [ "$pid" != "" ] && kill $pid
 }
 
-# netatalk.conf expects hostname in $HOSTNAME by default
+# default
+ATALK_NAME=`hostname|cut -d. -f1`
+ATALK_UNIX_CHARSET='LOCALE'
+ATALK_MAC_CHARSET='MAC_ROMAN'
+
+CNID_METAD_RUN=yes
+AFPD_RUN=yes
+AFPD_MAX_CLIENTS=20
+AFPD_UAMLIST="-U uams_dhx.so,uams_dhx2.so"
+AFPD_GUEST=nobody
+CNID_CONFIG="-l log_note"
+
+ATALKD_RUN=no
+PAPD_RUN=no
+TIMELORD_RUN=no
+#A2BOOT_RUN=no
+ATALK_ZONE=
+ATALK_BGROUND=no
+
+# old netatalk.conf expected hostname in $HOSTNAME by default
 HOSTNAME=`hostname`
 
 . :ETCDIR:/netatalk.conf
@@ -67,7 +86,7 @@ atalk_startup() {
 case "$1" in
 
 'start')
-        if [ x"${ATALK_BGROUND}" = x"yes" ]; then
+        if [ x"${ATALK_BGROUND}" = x"yes" -a x"${ATALKD_RUN}" != x"no" ]; then
             echo "Starting netatalk in the background ... "
             atalk_startup > /dev/null &
         else
diff --git a/distrib/rpm/buildrpm b/distrib/rpm/buildrpm
deleted file mode 100755 (executable)
index 5ef90f3..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/bin/sh
-#
-# buildrpm
-# $Id: buildrpm,v 1.3 2003-01-11 17:26:06 jmarcus Exp $
-#
-# automates the process of building the netatalk rpm
-#
-# To properly bootstrap the RPM from a raw CVS pull,
-# place the CVS sandbox under, e.g. /usr/src/redhat/BUILD
-# and name this new directory 'netatalk-$version' (where
-# $version is the contents of the 'VERSION' file under the source
-# root).  Then, cd into the source root and run 'autogen.sh'
-# (with no arguments).  Finally, copy this file to the BUILD
-# directory and run it from there, passing the full name of the
-# source directory as the sole argument.
-#
-if [ "x$1" = "x" ]; then
-    echo "To avoid problems with builds on remote filesystems"
-    echo "please copy this file to your redhat/BUILD directory"
-    echo "and execute as 'buildrpm netatalk-xxy', using the actual"
-    echo "full name (i.e. with version) of the source tree."
-    exit 1
-fi
-
-CVSNAME=$1
-
-REDHAT_DIR=../
-
-VERSION=`cat $CVSNAME/VERSION`
-
-sed -e "s/__VERSION__/$VERSION/" \
-    $CVSNAME/distrib/rpm/netatalk-redhat.spec \
-    > ${REDHAT_DIR}/SPECS/netatalk.spec
-
-cp -f $CVSNAME/distrib/rpm/netatalk-rpmbuild.patch \
-    ${REDHAT_DIR}/SOURCES
-
-# Newer distros use rpmbuild
-if `rpmbuild --version > /dev/null`; then
-    RPM="rpmbuild"
-else
-    RPM="rpm"
-fi
-
-# clean out objects and Makefiles
-(cd $CVSNAME && make distclean)
-
-# tar up the archive
-tar -c -v -z -f ${REDHAT_DIR}/SOURCES/$CVSNAME.tar.gz \
-       --exclude="*/CVS" --exclude="*~" $CVSNAME
-
-# build the SRPM and binary and devel RPMs.
-${RPM} -ba ${REDHAT_DIR}/SPECS/netatalk.spec
diff --git a/distrib/rpm/netatalk-asun.spec.old b/distrib/rpm/netatalk-asun.spec.old
deleted file mode 100644 (file)
index 4e3d992..0000000
+++ /dev/null
@@ -1,297 +0,0 @@
-Summary: AppleTalk networking programs
-Name: netatalk
-Version: 1.4b2+asun2.1.4
-Release: pre39
-Packager: iNOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-Copyright: BSD
-Group: Networking
-Source0: ftp://ftp.cobaltnet.com/pub/users/asun/testing/pre-asun2.1.4-36.tar.gz
-Patch0: netatalk-asun.makefile.patch
-Requires: pam >= 0.56
-BuildRoot: /var/tmp/atalk
-
-%description
-This package enables Linux to talk to Macintosh computers via the
-AppleTalk networking protocol. It includes a daemon to allow Linux
-to act as a file server over AppleTalk or IP for Mac's.
-
-%package devel
-Summary: Headers and static libraries for Appletalk development
-Group: Development/Libraries
-
-%description devel
-This packge contains the header files, and static libraries for building
-Appletalk networking programs.
-
-%prep
-%setup
-%patch0 -p1
-
-%build
-make OPTOPTS="$RPM_OPT_FLAGS -fomit-frame-pointer -fsigned-char" OSVERSION=2.0
-
-%install
-rm -rf $RPM_BUILD_ROOT
-mkdir -p $RPM_BUILD_ROOT/etc/atalk
-mkdir -p $RPM_BUILD_ROOT/etc/pam.d
-mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
-for i in 0 1 2 3 4 5 6; do
-       mkdir -p $RPM_BUILD_ROOT/etc/rc.d/rc$i.d
-done
-mkdir -p $RPM_BUILD_ROOT/usr/lib/atalk
-
-make install INSTALL_PREFIX=$RPM_BUILD_ROOT
-
-for i in aecho getzones megatron nbplkup nbprgstr nbpunrgstr pap \
-       papstatus psorder; do
-       strip $RPM_BUILD_ROOT/usr/bin/$i
-done
-for i in afpd atalkd psf psa papd; do
-       strip $RPM_BUILD_ROOT/usr/sbin/$i
-done
-
-install -m644 config/AppleVolumes.system $RPM_BUILD_ROOT/etc/atalk/AppleVolumes.system
-install -m644 config/AppleVolumes.default $RPM_BUILD_ROOT/etc/atalk/AppleVolumes.default
-install -m644 config/atalkd.conf $RPM_BUILD_ROOT/etc/atalk/atalkd.conf
-install -m644 config/papd.conf $RPM_BUILD_ROOT/etc/atalk/papd.conf
-
-# This is not necessary because chkconfig will make the links.
-for i in 0 1 2 6; do
-       ln -sf ../init.d/atalk $RPM_BUILD_ROOT/etc/rc.d/rc$i.d/K35atalk
-done
-for i in 3 4 5; do
-       ln -sf ../init.d/atalk $RPM_BUILD_ROOT/etc/rc.d/rc$i.d/S91atalk
-done
-
-%clean
-rm -rf $RPM_BUILD_ROOT
-
-%post
-/sbin/chkconfig --add atalk
-ldconfig
-# Do only for the first install
-if [ "$1" = 1 ] ; then
-  # Add the ddp lines to /etc/services
-  if (grep '[0-9][0-9]*/ddp' /etc/services >/dev/null); then
-    cat <<'_EOD1_' >&2
-warning: The DDP services appear to be present in /etc/services.
-warning: Please check them against services.atalk in the documentation.
-_EOD1_
-    true
-  else
-    cat <<'_EOD2_' >>/etc/services
-# start of DDP services
-#
-# Everything between the 'start of DDP services' and 'end of DDP services'
-# lines will be automatically deleted when the netatalk package is removed.
-#
-rtmp           1/ddp           # Routing Table Maintenance Protocol
-nbp            2/ddp           # Name Binding Protocol
-echo           4/ddp           # AppleTalk Echo Protocol
-zip            6/ddp           # Zone Information Protocol
-
-afpovertcp     548/tcp         # AFP over TCP
-afpovertcp     548/udp
-# end of DDP services
-_EOD2_
-  fi
-fi
-
-%postun
-# Do only for the last un-install
-if [ "$1" = 0 ] ; then
-  /sbin/chkconfig --del atalk
-  # remove the ddp lines from /etc/services
-  if (grep '^# start of DDP services$' /etc/services >/dev/null && \
-      grep '^# end of DDP services$' /etc/services >/dev/null ); then
-    sed -e '/^# start of DDP services$/,/^# end of DDP services$/d' \
-       </etc/services >/tmp/services.tmp$$
-    cat /tmp/services.tmp$$ >/etc/services
-    rm /tmp/services.tmp$$
-  else
-    cat <<'_EOD3_' >&2
-warning: Unable to find the lines `# start of DDP services' and
-warning: `# end of DDP services' in the file /etc/services.
-warning: You should remove the DDP services from /etc/service manually.
-_EOD3_
-  fi
-fi
-
-%files
-%doc BUGS CHANGES CONTRIBUTORS COPYRIGHT ChangeLog INSTALL/ README* TODO VERSION contrib/ services.atalk
-%dir /etc/atalk
-%config /etc/atalk/AppleVolumes.default
-%config /etc/atalk/AppleVolumes.system
-%config /etc/atalk/netatalk.conf
-%config /etc/atalk/afpd.conf
-%config /etc/atalk/atalkd.conf
-%config /etc/atalk/papd.conf
-%config /etc/rc.d/init.d/atalk
-%config /etc/pam.d/netatalk
-/etc/rc.d/rc0.d/K35atalk
-/etc/rc.d/rc1.d/K35atalk
-/etc/rc.d/rc2.d/K35atalk
-/etc/rc.d/rc3.d/S91atalk
-/etc/rc.d/rc4.d/S91atalk
-/etc/rc.d/rc5.d/S91atalk
-/etc/rc.d/rc6.d/K35atalk
-/usr/sbin/afpd
-/usr/sbin/atalkd
-/usr/sbin/papd
-/usr/sbin/psa
-/usr/sbin/etc2ps
-/usr/sbin/psf
-/usr/bin/adv1tov2
-/usr/bin/aecho
-/usr/bin/afppasswd
-/usr/bin/binheader
-/usr/bin/getzones
-/usr/bin/hqx2bin
-/usr/bin/macbinary
-/usr/bin/megatron
-/usr/bin/nadheader
-/usr/bin/nbplkup
-/usr/bin/nbprgstr
-/usr/bin/nbpunrgstr
-/usr/bin/pap
-/usr/bin/papstatus
-/usr/bin/psorder
-/usr/bin/single2bin
-/usr/bin/unbin
-/usr/bin/unhex
-/usr/bin/unsingle
-%dir /usr/lib/atalk
-/usr/lib/atalk/filters/
-/usr/lib/atalk/nls/
-/usr/lib/atalk/pagecount.ps
-/usr/lib/atalk/uams/
-/usr/man/man1/aecho.1
-/usr/man/man1/getzones.1
-/usr/man/man1/hqx2bin.1
-/usr/man/man1/macbinary.1
-/usr/man/man1/megatron.1
-/usr/man/man1/nbp.1
-/usr/man/man1/nbplkup.1
-/usr/man/man1/nbprgstr.1
-/usr/man/man1/pap.1
-/usr/man/man1/papstatus.1
-/usr/man/man1/psorder.1
-/usr/man/man1/single2bin.1
-/usr/man/man1/unbin.1
-/usr/man/man1/unhex.1
-/usr/man/man1/unsingle.1
-/usr/man/man3/atalk_aton.3
-/usr/man/man3/nbp_name.3
-/usr/man/man4/atalk.4
-/usr/man/man8/afpd.8
-/usr/man/man8/atalkd.8
-/usr/man/man8/papd.8
-/usr/man/man8/psf.8
-
-%files devel
-/usr/lib/libatalk.a
-/usr/lib/libatalk_p.a
-/usr/include/atalk/
-/usr/include/netatalk/
-
-%changelog
-* Thu Jul 22 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- /etc/atalk/netatalk.config -> /etc/atalk/netatalk.conf
-  Many parts of patch are merged into the original source code.
-* Tue Jul 13 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- AppleVolumes.system is merged into the original source code.
-  /etc/atalk/config -> /etc/atalk/netatalk.config.
-  Merge original rc.atalk.redhat and /etc/rc.d/init.d/atalk.
-  Remove last sample line of patched afpd.conf.
-* Fri Jul 9 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- [pre-asun2.1.4-30]
-* Sun Jun 20 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- [pre-asun2.1.4-28]
-* Thu Jun 3 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- [pre-asun2.1.4-22]
-* Wed May 19 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- [pre-asun2.1.4-15]
-  Make BerkleyDB=/usr.
-* Sun May 2 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- [pre-asun2.1.4-11]
-  Integrate three patches into netatalk-asun.makefile.patch.
-  Change /etc/uams dir to /usr/lib/atalk/uams.
-  Add configuration line to /etc/atalk/afpd.conf and remove needless 
-  variables from /etc/atalk/config and /etc/rc.d/init.d/atalk.
-* Wed Apr 21 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- [pre-asun2.1.4-9]
-  Move %chengelog section last.
-* Wed Mar 31 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- Comment out -DNEED_QUOTA_WRAPPER in sys/linux/Makefile.
-* Sat Mar 20 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- Correct symbolic links to psf.
-  Remove asciize function from nbplkup so as to display Japanese hostname.
-* Thu Mar 11 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- Included MacPerl 5 script ICDumpSuffixMap which dumps suffix mapping
-  containd in Internet Config Preference.
-* Tue Mar 2 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- [asun2.1.3]
-* Mon Feb 15 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- [pre-asun2.1.2-8]
-* Sun Feb 7 1999 iNOUE Koich! <inoue@ma.ns.musashi-tech.ac.jp>
-- [pre-asun2.1.2-6]
-* Mon Jan 25 1999 iNOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- [pre-asun2.1.2-3]
-* Thu Dec 17 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- [pre-asun2.1.2]
-  Remove crlf patch. It is now a server's option.
-* Thu Dec 3 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use stable version source netatalk-1.4b2+asun2.1.1.tar.gz
-  Add uams directory
-* Sat Nov 28 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use pre-asun2.1.1-3 source.
-* Mon Nov 23 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use pre-asun2.1.1-2 source.
-* Mon Nov 16 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Fix rcX.d's symbolic links.
-* Wed Oct 28 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use pre-asun2.1.0a-2 source. Remove '%exclusiveos linux' line.
-* Sat Oct 24 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use stable version source netatalk-1.4b2+asun2.1.0.tar.gz.
-* Mon Oct 5 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use pre-asun2.1.0-10a source.
-* Thu Sep 19 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use pre-asun2.1.0-8 source. Add chkconfig support.
-* Sat Sep 12 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Comment out -DCRLF. Use RPM_OPT_FLAGS.
-* Mon Sep 8 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use pre-asun2.1.0-7 source. Rename atalk.init to atalk.
-* Mon Aug 22 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use pre-asun2.1.0-6 source.
-* Mon Jul 27 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use pre-asun2.1.0-5 source.
-* Tue Jul 21 1998 INOUE Koichi <inoue@ma.ns.musashi-techa.c.jp>
-- Use pre-asun2.1.0-3 source.
-* Tue Jul 7 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Add afpovertcp entries to /etc/services
-- Remove BuildRoot in man8 pages
-* Mon Jun 29 1998 INOUE Koichi <inoue@ma.ns.musashi-tech.ac.jp>
-- Use modified sources 1.4b2+asun2.1.0 produced by Adrian Sun
-  <asun@saul9.u.washington.edu> to provide an AppleShareIP file server
-- Included AppleVolumes.system file maintained by Johnson
-  <johnson@stpt.usf.edu>
-* Mon Aug 25 1997 David Gibson <D.Gibson@student.anu.edu.au>
-- Used a buildroot
-- Use RPM_OPT_FLAGS
-- Moved configuration parameters/files from atalk.init to /etc/atalk
-- Separated devel package
-- Built with shared libraries
-* Sun Jul 13 1997 Paul H. Hargrove <hargrove@sccm.Stanford.EDU>
-- Updated sources from 1.3.3 to 1.4b2
-- Included endian patch for Linux/SPARC
-- Use all the configuration files supplied in the source.  This has the
-  following advantages over the ones in the previous rpm release:
-       + The printer 'lp' isn't automatically placed in papd.conf
-       + The default file conversion is binary rather than text.
-- Automatically add and remove DDP services from /etc/services
-- Placed the recommended /etc/services in the documentation
-- Changed atalk.init to give daemons a soft kill
-- Changed atalk.init to make configuration easier
-
-* Wed May 28 1997 Mark Cornick <mcornick@zorak.gsfc.nasa.gov>
-Updated for /etc/pam.d
diff --git a/distrib/rpm/netatalk-fedora.spec b/distrib/rpm/netatalk-fedora.spec
deleted file mode 100644 (file)
index ca31a1b..0000000
+++ /dev/null
@@ -1,194 +0,0 @@
-#################################################### VERSIONING INFORMATION
-%define name    netatalk
-%define version 2.0.2
-%define release 2
-
-################################################# BASIC PACKAGE INFORMATION
-Summary: Appletalk and Appleshare/IP services for Linux
-Name: %{name}
-Version: %{version}
-Release: %{release}
-Copyright: BSD
-Group: Networking/Daemons
-Source0: %{name}-%{version}.tar.gz
-URL: http://netatalk.sourceforge.net/
-Packager: dan dickey <dan.dickey@savvis.net>
-
-############################################################## REQUIREMENTS
-Requires: cracklib, openssl, tcp_wrappers, pam
-BuildRequires: openssl-devel
-
-Prefix:    %{_prefix}
-BuildRoot: /var/tmp/%{name}-buildroot
-
-%description
-netatalk is an implementation of the AppleTalk Protocol Suite for Unix/Linux
-systems. The current release contains support for Ethertalk Phase I and II,
-DDP, RTMP, NBP, ZIP, AEP, ATP, PAP, ASP, and AFP. It provides Appletalk file
-printing and routing services on Solaris 2.5, Linux, FreeBSD, SunOS 4.1 and
-Ultrix 4. It also supports AFP 3, 2.2 and 2.1 (Appleshare IP).
-
-%package devel
-Group: Development/Networking
-Summary: Appletalk and Appleshare/IP services for Linux development files
-%description devel
-netatalk is an implementation of the AppleTalk Protocol Suite for Unix/Linux
-systems. The current release contains support for Ethertalk Phase I and II,
-DDP, RTMP, NBP, ZIP, AEP, ATP, PAP, ASP, and AFP. It provides Appletalk file
-printing and routing services on Solaris 2.5, Linux, FreeBSD, SunOS 4.1 and
-Ultrix 4. It also supports AFP 3, 2.2 and 2.1 (Appleshare IP).
-
-This package is required for developing appletalk-based applications.
-
-%prep
-%setup -q -n %{name}-%{version}/
-
-%build
-CFLAGS="$RPM_OPT_FLAGS -fomit-frame-pointer -fsigned-char" ./configure \
-       --prefix=%{prefix} \
-       --libexec=%{prefix}/libexec/netatalk \
-       --with-config-dir=/etc/atalk \
-       --with-pkgconfdir=/etc/atalk \
-       --with-uams-path=/etc/atalk/uams \
-       --with-message-dir=/etc/atalk/msg \
-       --enable-lastdid \
-       --enable-redhat \
-       --with-cracklib \
-       --with-pam \
-       --with-shadow \
-       --with-tcp-wrappers \
-       --with-ssl \
-       --enable-pgp-uam \
-       --enable-a2boot
-make all
-
-%install
-### INSTALL (USING "make install") ###
-mkdir -p $RPM_BUILD_ROOT{%{prefix},/etc/atalk/{uams,msg}}
-make DESTDIR=$RPM_BUILD_ROOT install-strip
-
-%post
-### RUN CHKCONFIG ###
-/sbin/chkconfig --add atalk
-/sbin/ldconfig
-# after the first install only
-if [ "$1" = 1 ]; then
-       # add the ddp lines to /etc/services
-       if (grep '[0-9][0-9]*/ddp' /etc/services >/dev/null); then
-               cat <<'_EOD1_' >&2
-warning: The DDP services appear to be present in /etc/services.
-warning: Please check them against services.atalk in the documentation.
-_EOD1_
-               true
-       else
-               cat <<'_EOD2_' >>/etc/services
-# start of DDP services
-#
-# Everything between the 'start of DDP services' and 'end of DDP services'
-# lines will be automatically deleted when the netatalk package is removed.
-#
-rtmp           1/ddp           # Routing Table Maintenance Protocol
-nbp            2/ddp           # Name Binding Protocol
-echo           4/ddp           # AppleTalk Echo Protocol
-zip            6/ddp           # Zone Information Protocol
-
-afpovertcp     548/tcp         # AFP over TCP
-afpovertcp     548/udp
-# end of DDP services
-_EOD2_
-       fi
-fi
-
-%preun
-### RUN CHKCONFIG ###
-/sbin/chkconfig --del atalk
-
-%postun
-# do only for the last un-install
-if [ "$1" = 0 ]; then
-       # remove the ddp lines from /etc/services
-       if (grep '^# start of DDP services$' /etc/services >/dev/null && \
-           grep '^# end of DDP services$'   /etc/services >/dev/null ); then
-         sed -e '/^# start of DDP services$/,/^# end of DDP services$/d' \
-           </etc/services >/tmp/services.tmp$$
-         cat /tmp/services.tmp$$ >/etc/services
-         rm /tmp/services.tmp$$
-       else
-         cat <<'_EOD3_' >&2
-warning: Unable to find the lines `# start of DDP services` and
-warning: `# end of DDP services` in the file /etc/services.
-warning: You should remove the DDP services from /etc/services manually.
-_EOD3_
-       fi
-fi
-
-%clean
-[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
-
-%files
-%defattr(-,root,root)
-%doc doc/[A-L,N-Z]*
-%config /etc/atalk/Apple*
-%config /etc/atalk/*.conf
-%config /etc/pam.d/netatalk
-%dir /etc/atalk
-%dir /etc/atalk/msg
-%dir /etc/atalk/uams
-/etc/atalk/uams/*.so
-/etc/rc.d/init.d/atalk
-%{prefix}/bin/*
-%{prefix}/sbin/*
-%{prefix}/libexec/*
-%{prefix}/man/man*/*.gz
-%{prefix}/share/netatalk/pagecount.ps
-
-%files devel
-%defattr(-,root,root)
-%{prefix}/lib/*.a
-%{prefix}/lib/*.la
-/etc/atalk/uams/*.a
-/etc/atalk/uams/*.la
-%dir %{prefix}/include/atalk
-%{prefix}/include/atalk/*.h
-%dir %{prefix}/include/netatalk
-%{prefix}/include/netatalk/*.h
-%{prefix}/share/aclocal/netatalk.m4
-
-%changelog
-
-* Thu Apr 28 2005 Dan A. Dickey <dan.dickey@savvis.net>
-  - Modify redhat spec file for Fedora Core.
-
-* Sat Jan 04 2002 Steven N. Hirsch <shirsch@adelphia.net>
-  - Fix RedHat RPM build.
-  - Build Apple2 boot support.
-
-* Thu Apr 12 2001 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-  - v1.5pre6-1
-  - pre-release 6 for sourceforge
-
-* Wed Mar 07 2001 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-  - v1.5pre5-1
-  - pre-release 5 for sourceforge
-
-* Fri Feb 23 2001 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-  - v1.5pre5-0
-  - pre-release 5 for sourceforge (prebuild)
-
-* Tue Feb 20 2001 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-  - v1.5pre4-1
-  - pre-release 4 for sourceforge
-  - modified/split mandrake spec for redhat 7 build
-
-* Mon Dec 18 2000 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-  - v1.5pre3-1mdk
-  - pre-release 3 for sourceforge
-  - moved away from 1.4.99 ... 
-
-* Wed Nov 08 2000 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-  - v1.4.99-0.20001108mdk
-  - pre-release 2 for sourceforge
-
-* Wed Sep 27 2000 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-  - v1.4.99-0.20000927mdk
-  - pre-release 1 for sourceforge
diff --git a/distrib/rpm/netatalk-mandrake.spec b/distrib/rpm/netatalk-mandrake.spec
deleted file mode 100644 (file)
index caa0cd8..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-#################################################### VERSIONING INFORMATION
-%define name    netatalk
-%define version 1.5pre6
-%define release 1mdk
-%define tardir %{name}-%{version}
-
-################################################# BASIC PACKAGE INFORMATION
-Summary: Appletalk and Appleshare/IP services for Linux
-Name: %{name}
-Version: %{version}
-Release: %{release}
-Copyright: BSD
-Group: Networking/Daemons
-Source0: %{name}-%{version}.tar.bz2
-URL: http://netatalk.sourceforge.net/
-Packager: rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-Obsoletes: netatalk-1.4b2+asun netatalk-1.4.99
-
-############################################################## REQUIREMENTS
-Requires: cracklib, openssl, tcp_wrappers, pam
-BuildRequires: cracklib-devel, openssl-devel, pam-devel
-
-Prefix:    %{_prefix}
-BuildRoot: %{_tmppath}/%{name}-buildroot
-
-%description
-netatalk is an implementation of the AppleTalk Protocol Suite for Unix/Linux
-systems. The current release contains support for Ethertalk Phase I and II,
-DDP, RTMP, NBP, ZIP, AEP, ATP, PAP, ASP, and AFP. It provides Appletalk file
-printing and routing services on Solaris 2.5, Linux, FreeBSD, SunOS 4.1 and
-Ultrix 4. It also supports AFP 2.1 and 2.2 (Appleshare IP).
-
-%package devel
-Group: Development/Networking
-Summary: Appletalk and Appleshare/IP services for Linux development files
-%description devel
-netatalk is an implementation of the AppleTalk Protocol Suite for Unix/Linux
-systems. The current release contains support for Ethertalk Phase I and II,
-DDP, RTMP, NBP, ZIP, AEP, ATP, PAP, ASP, and AFP. It provides Appletalk file
-printing and routing services on Solaris 2.5, Linux, FreeBSD, SunOS 4.1 and
-Ultrix 4. It also supports AFP 2.1 and 2.2 (Appleshare IP).
-
-This package is required for developing appletalk-based applications.
-
-%changelog
-
-* Thu Apr 12 2001 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-  - v1.5pre6-1mdk
-  - pre-release 6 for sourceforge
-
-* Wed Mar 07 2001 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-  - v1.5pre5-1mdk
-  - pre-release 5 for sourceforge
-  - sync with redhat package
-
-* Mon Dec 18 2000 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-  - v1.5pre3-1mdk
-  - pre-release 3 for sourceforge
-  - moved away from 1.4.99 ... 
-
-* Wed Nov 08 2000 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-  - v1.4.99-0.20001108mdk
-  - pre-release 2 for sourceforge
-
-* Wed Sep 27 2000 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-  - v1.4.99-0.20000927mdk
-  - pre-release 1 for sourceforge
-
-%prep
-%setup -q -n %{tardir}/
-
-%build
-export LD_PRELOAD=
-CFLAGS="$RPM_OPT_FLAGS -fomit-frame-pointer -fsigned-char" ./configure \
-       --prefix=%{prefix} \
-    --with-config-dir=/etc/atalk \
-       --with-uams-path=/etc/atalk/uams \
-    --with-message-dir=/etc/atalk/msg \
-       --enable-lastdid \
-       --enable-redhat \
-       --with-cracklib \
-       --with-pam \
-       --with-shadow \
-       --with-tcp-wrappers \
-       --with-ssl \
-       --enable-pgp-uam
-make all
-
-%install
-### INSTALL (USING "make install") ###
-mkdir -p $RPM_BUILD_ROOT{%{prefix},/etc/atalk/{uams,msg}}
-make DESTDIR=$RPM_BUILD_ROOT install-strip
-
-# bzip2 man pages
-for i in 1 3 4 5 8; do
-       bzip2 -v $RPM_BUILD_ROOT/usr/man/man$i/*.$i
-done
-
-%post
-### RUN CHKCONFIG ###
-/sbin/chkconfig --add atalk
-/sbin/ldconfig
-# after the first install only
-if [ "$1" = 1 ]; then
-       # add the ddp lines to /etc/services
-       if (grep '[0-9][0-9]*/ddp' /etc/services >/dev/null); then
-               cat <<'_EOD1_' >&2
-warning: The DDP services appear to be present in /etc/services.
-warning: Please check them against services.atalk in the documentation.
-_EOD1_
-               true
-       else
-               cat <<'_EOD2_' >>/etc/services
-# start of DDP services
-#
-# Everything between the 'start of DDP services' and 'end of DDP services'
-# lines will be automatically deleted when the netatalk package is removed.
-#
-rtmp           1/ddp           # Routing Table Maintenance Protocol
-nbp            2/ddp           # Name Binding Protocol
-echo           4/ddp           # AppleTalk Echo Protocol
-zip            6/ddp           # Zone Information Protocol
-
-afpovertcp     548/tcp         # AFP over TCP
-afpovertcp     548/udp
-# end of DDP services
-_EOD2_
-       fi
-fi
-
-%preun
-### RUN CHKCONFIG ###
-/sbin/chkconfig --del atalk
-
-%postun
-# do only for the last un-install
-if [ "$1" = 0 ]; then
-       # remove the ddp lines from /etc/services
-       if (grep '^# start of DDP services$' /etc/services >/dev/null && \
-           grep '^# end of DDP services$'   /etc/services >/dev/null ); then
-         sed -e '/^# start of DDP services$/,/^# end of DDP services$/d' \
-           </etc/services >/tmp/services.tmp$$
-         cat /tmp/services.tmp$$ >/etc/services
-         rm /tmp/services.tmp$$
-       else
-         cat <<'_EOD3_' >&2
-warning: Unable to find the lines `# start of DDP services` and
-warning: `# end of DDP services` in the file /etc/services.
-warning: You should remove the DDP services from /etc/services manually.
-_EOD3_
-       fi
-fi
-
-%clean
-rm -rf $RPM_BUILD_ROOT
-rm -rf $RPM_BUILD_DIR/%{tardir}/
-
-%files
-%defattr(-,root,root)
-%doc [A-Z][A-Z]* ChangeLog doc/[A-Z][A-Z]*
-%dir /etc/atalk
-%dir /etc/atalk/msg
-%config /etc/atalk/Apple*
-%config /etc/atalk/*.conf
-%config /etc/pam.d/netatalk
-%dir /etc/atalk/nls
-/etc/atalk/nls/*
-%dir /etc/atalk/uams
-/etc/atalk/uams/*.so
-/etc/rc.d/init.d/atalk
-%{prefix}/bin/*
-%{prefix}/sbin/*
-%{prefix}/man/man*/*
-
-%files devel
-%defattr(-,root,root)
-%{prefix}/lib/*.a
-%dir %{prefix}/include/atalk
-%{prefix}/include/atalk/*.h
-%dir %{prefix}/include/netatalk
-%{prefix}/include/netatalk/*.h
-%{prefix}/share/aclocal/netatalk.m4
diff --git a/distrib/rpm/netatalk-redhat.spec b/distrib/rpm/netatalk-redhat.spec
deleted file mode 100644 (file)
index b2c8a3e..0000000
+++ /dev/null
@@ -1,194 +0,0 @@
-#################################################### VERSIONING INFORMATION
-%define name    netatalk
-%define version __VERSION__
-%define release 1
-
-################################################# BASIC PACKAGE INFORMATION
-Summary: Appletalk and Appleshare/IP services for Linux
-Name: %{name}
-Version: %{version}
-Release: %{release}
-Copyright: BSD
-Group: Networking/Daemons
-Source0: %{name}-%{version}.tar.gz
-Patch0: netatalk-rpmbuild.patch
-URL: http://netatalk.sourceforge.net/
-Packager: rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-Obsoletes: netatalk-1.4b2+asun netatalk-1.4.99
-
-############################################################## REQUIREMENTS
-Requires: cracklib, openssl, tcp_wrappers, pam
-BuildRequires: openssl-devel
-
-# Note: RedHat 7.3 build requires autoconf >= 2.53, automake >= 1.5, ac-archive >= 0.5
-
-Prefix:    %{_prefix}
-BuildRoot: /var/tmp/%{name}-buildroot
-
-%description
-netatalk is an implementation of the AppleTalk Protocol Suite for Unix/Linux
-systems. The current release contains support for Ethertalk Phase I and II,
-DDP, RTMP, NBP, ZIP, AEP, ATP, PAP, ASP, and AFP. It provides Appletalk file
-printing and routing services on Solaris 2.5, Linux, FreeBSD, SunOS 4.1 and
-Ultrix 4. It also supports AFP 2.1 and 2.2 (Appleshare IP).
-
-%package devel
-Group: Development/Networking
-Summary: Appletalk and Appleshare/IP services for Linux development files
-%description devel
-netatalk is an implementation of the AppleTalk Protocol Suite for Unix/Linux
-systems. The current release contains support for Ethertalk Phase I and II,
-DDP, RTMP, NBP, ZIP, AEP, ATP, PAP, ASP, and AFP. It provides Appletalk file
-printing and routing services on Solaris 2.5, Linux, FreeBSD, SunOS 4.1 and
-Ultrix 4. It also supports AFP 2.1 and 2.2 (Appleshare IP).
-
-This package is required for developing appletalk-based applications.
-
-%changelog
-
-* Sat Jan 04 2002 Steven N. Hirsch <shirsch@adelphia.net>
-  - Fix RedHat RPM build.
-  - Build Apple2 boot support.
-
-* Thu Apr 12 2001 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-  - v1.5pre6-1
-  - pre-release 6 for sourceforge
-
-* Wed Mar 07 2001 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-  - v1.5pre5-1
-  - pre-release 5 for sourceforge
-
-* Fri Feb 23 2001 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-  - v1.5pre5-0
-  - pre-release 5 for sourceforge (prebuild)
-
-* Tue Feb 20 2001 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-  - v1.5pre4-1
-  - pre-release 4 for sourceforge
-  - modified/split mandrake spec for redhat 7 build
-
-* Mon Dec 18 2000 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-  - v1.5pre3-1mdk
-  - pre-release 3 for sourceforge
-  - moved away from 1.4.99 ... 
-
-* Wed Nov 08 2000 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-  - v1.4.99-0.20001108mdk
-  - pre-release 2 for sourceforge
-
-* Wed Sep 27 2000 rufus t firefly <rufus.t.firefly@linux-mandrake.com>
-  - v1.4.99-0.20000927mdk
-  - pre-release 1 for sourceforge
-
-%prep
-%setup -q -n %{name}-%{version}/
-%patch0 -p1 -b .rpmbuild
-
-%build
-CFLAGS="$RPM_OPT_FLAGS -fomit-frame-pointer -fsigned-char" ./configure \
-       --prefix=%{prefix} \
-       --libexec=%{prefix}/libexec/netatalk \
-       --with-config-dir=/etc/atalk \
-       --with-pkgconfdir=/etc/atalk \
-       --with-uams-path=/etc/atalk/uams \
-       --with-message-dir=/etc/atalk/msg \
-       --enable-lastdid \
-       --enable-redhat \
-       --with-cracklib \
-       --with-pam \
-       --with-shadow \
-       --with-tcp-wrappers \
-       --with-ssl \
-       --enable-pgp-uam \
-       --enable-a2boot
-make all
-
-%install
-### INSTALL (USING "make install") ###
-mkdir -p $RPM_BUILD_ROOT{%{prefix},/etc/atalk/{uams,msg}}
-make DESTDIR=$RPM_BUILD_ROOT install-strip
-
-%post
-### RUN CHKCONFIG ###
-/sbin/chkconfig --add atalk
-/sbin/ldconfig
-# after the first install only
-if [ "$1" = 1 ]; then
-       # add the ddp lines to /etc/services
-       if (grep '[0-9][0-9]*/ddp' /etc/services >/dev/null); then
-               cat <<'_EOD1_' >&2
-warning: The DDP services appear to be present in /etc/services.
-warning: Please check them against services.atalk in the documentation.
-_EOD1_
-               true
-       else
-               cat <<'_EOD2_' >>/etc/services
-# start of DDP services
-#
-# Everything between the 'start of DDP services' and 'end of DDP services'
-# lines will be automatically deleted when the netatalk package is removed.
-#
-rtmp           1/ddp           # Routing Table Maintenance Protocol
-nbp            2/ddp           # Name Binding Protocol
-echo           4/ddp           # AppleTalk Echo Protocol
-zip            6/ddp           # Zone Information Protocol
-
-afpovertcp     548/tcp         # AFP over TCP
-afpovertcp     548/udp
-# end of DDP services
-_EOD2_
-       fi
-fi
-
-%preun
-### RUN CHKCONFIG ###
-/sbin/chkconfig --del atalk
-
-%postun
-# do only for the last un-install
-if [ "$1" = 0 ]; then
-       # remove the ddp lines from /etc/services
-       if (grep '^# start of DDP services$' /etc/services >/dev/null && \
-           grep '^# end of DDP services$'   /etc/services >/dev/null ); then
-         sed -e '/^# start of DDP services$/,/^# end of DDP services$/d' \
-           </etc/services >/tmp/services.tmp$$
-         cat /tmp/services.tmp$$ >/etc/services
-         rm /tmp/services.tmp$$
-       else
-         cat <<'_EOD3_' >&2
-warning: Unable to find the lines `# start of DDP services` and
-warning: `# end of DDP services` in the file /etc/services.
-warning: You should remove the DDP services from /etc/services manually.
-_EOD3_
-       fi
-fi
-
-%clean
-rm -rf $RPM_BUILD_ROOT
-rm -rf $RPM_BUILD_DIR/%{name}/
-
-%files
-%defattr(-,root,root)
-%doc doc/[A-L,N-Z]*
-%config /etc/atalk/Apple*
-%config /etc/atalk/*.conf
-%config /etc/pam.d/netatalk
-/etc/atalk/nls/*
-/etc/atalk/uams/*.so
-/etc/rc.d/init.d/atalk
-%dir /etc/atalk
-%dir /etc/atalk/nls
-%dir /etc/atalk/uams
-%{prefix}/bin/*
-%{prefix}/sbin/*
-%{prefix}/libexec/*
-%{prefix}/man/man*/*.gz
-
-%files devel
-%defattr(-,root,root)
-%{prefix}/lib/*.a
-%dir %{prefix}/include/atalk
-%{prefix}/include/atalk/*.h
-%dir %{prefix}/include/netatalk
-%{prefix}/include/netatalk/*.h
-%{prefix}/share/aclocal/netatalk.m4
diff --git a/distrib/rpm/netatalk-rpmbuild.patch b/distrib/rpm/netatalk-rpmbuild.patch
deleted file mode 100644 (file)
index f965b2f..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
---- netatalk-1.7cvs/bin/afile/Makefile.in.orig 2003-01-07 20:44:23.000000000 -0500
-+++ netatalk-1.7cvs/bin/afile/Makefile.in      2003-01-08 07:09:05.000000000 -0500
-@@ -117,12 +117,12 @@
- install_sh = @install_sh@
- bin_PROGRAMS = afile achfile
--bin_SCRIPTS = acleandir.rc
-+bin_SCRIPTS = # acleandir.rc
- afile_SOURCES = afile.c common.c common.h
- achfile_SOURCES = achfile.c common.c common.h
--EXTRA_DIST = acleandir.rc
-+EXTRA_DIST = # acleandir.rc
- subdir = bin/afile
- mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
- CONFIG_HEADER = $(top_builddir)/config.h
index 39eecd248a42ba5fea316b2ec44eb1b2c1168622..9ba1765bcf4fa9364f8be79be7f9349beb84a89b 100644 (file)
@@ -144,16 +144,82 @@ Author: Andrew Morgan <morgan@linux.kernel.org>
 
 Linux-PAM is a suite of shared libraries that enable the local system
 administrator to choose how applications authenticate users.
-
 You can get the Linux PAM documentation and sources from
 http://www.kernel.org/pub/linux/libs/pam/
-
 Netatalk also supports other standard PAM implementations such as OpenPAM.
 
-8 Berkeley DB
+8. Berkeley DB
 Berkeley DB is a programmatic toolkit that provides fast, reliable,
 scalable, and mission-critical database support to software
 developers. BDB can downloaded from
 http://www.oracle.com/database/berkeley-db/index.html
 Netatalk's CNID database uses the library and header files from BDB.
 Currently, Netatalk supports BDB 4.6 and later.
+
+Error checking and logging
+==========================
+We wan't rigid error checking and concise log messages. This often leads
+to signifant code bloat where the relevant function call is buried in error
+checking and logging statements.
+In order to alleviate error checking and code readability, we provide a set
+of error checking macros in <atalk/errchk.h>. These macros compare the return
+value of statements againt 0, NULL, -1 (and maybe more, check it out).
+Every macro comes in four flavours: EC_CHECK, EC_CHECK_LOG, EC_CHECK_LOG_ERR
+and EC_CHECK_CUSTOM:
+- EC_CHECK just checks the CHECK
+- EC_CHECK_LOG additionally logs the stringified function call.
+- EC_CHECK_LOG_ERR allows specifying the return value
+- EC_CHECK_CUSTOM allows custom actions
+The macros EC_CHECK* unconditionally jump to a cleanup label where the
+neccessary cleanup can be done alongside controlling the return value.
+EC_CHECK_CUSTOM doesn't do that, so an extra "goto EC_CLEANUP" may be
+performed as appropiate.
+
+Example:
+- stat() without EC macro:
+  static int func(const char *name) {
+    int ret = 0;
+    ...
+    if ((ret = stat(name, &some_struct_stat)) != 0) {
+      LOG(...);
+      ret = -1; /* often needed to explicitly set the error indicating return value */
+      goto cleanup;
+    }
+
+    return ret;
+
+  cleanup:
+    ...
+    return ret;
+  }
+
+- stat() with EC macro:
+  static int func(const char *name) {
+    EC_INIT; /* expands to int ret = 0; */
+
+    char *uppername = NULL
+    EC_NULL(uppername = strdup(name));
+    EC_ZERO(strtoupper(uppername));
+
+    EC_ZERO(stat(uppername, &some_struct_stat)); /* expands to complete if block from above */
+
+    EC_STATUS(0);
+
+EC_CLEANUP:
+    if (uppername) free(uppername);
+    EC_EXIT;
+  }
+
+A boileplate function template is:
+
+int func(void)
+{
+    EC_INIT;
+
+    ...your code here...
+
+    EC_STATUS(0);
+
+EC_CLEANUP:
+    EC_EXIT;
+}
diff --git a/doc/FAQ b/doc/FAQ
deleted file mode 100644 (file)
index 5092ac7..0000000
--- a/doc/FAQ
+++ /dev/null
@@ -1,636 +0,0 @@
-Netatalk Frequently Asked Questions
-($Id: FAQ,v 1.14 2010-04-25 13:59:53 hat001 Exp $)
-
------------------------------------------------------------------------------
-
-Q1: Where can I get more information on Netatalk?
-Q2: What is this I keep seeing about asun?
-Q3: How do I get the most recent version of Netatalk?
-Q4: Can I get an almost current version of Netatalk without having to learn Git?
-Q4a: Is there an RPM, package, or tarball for my platform?
-Q5: I'm having massive file deletion problems!
-Q6: I am having lots of file locking problems!
-Q7: I'm getting this message in my logs:
-     WARNING: DID conflict for ...  Are these the same file?
-Q8: I can't seem to use passwords longer than 8 characters for my netatalk
-    accounts. How can I fix that? 
-Q9: I would like to use encrypted passwords to authenticate to the Netatalk
-    server. How do I do that?
-Q10: How can I set who has access to certain directories?
-Q11: What are the .AppleDouble and .Parent directories which are created in
-     the netatalk locations?
-Q12: Hidden files - what's up with that?
-Q13: I get a "socket: Invalid argument" error when trying to start netatalk
-     under Linux. What is causing this?
-Q14: Netatalk works over Appletalk, but my IP connections are refused, even
-     though I have enabled them in the configuration files.
-Q15: I'm having Quark Express file locking problems, is there information on that?
-Q16: I'm getting this error in Quark Express when trying to save a file to
-     the server: 'Error Type -50'
-Q17: Does netatalk work with Mac OSX?
-Q18: I'm getting an 'Application for this document not found' error on OS X.
-Q19: I'm getting an 'Error Type -43' error on OS X.
-Q20: How do I get the directories that are created by Netatalk to have the
-     correct permissions by default?
-Q21:  What does this error mean:
-     'afpd[#####]: setdirmode: chmod .AppleDouble Operation not permitted'
-Q22: I'm having problems with the Trash folder: either when someone drags
-     files into it, the system want's them todelete them immeidately, or files
-     get stuck in there and won't delete.
-Q23: The daemons aren't starting, things aren't showing up in the Chooser,
-     and I get a message like this in the logs: afpd[####]: Can't register
-     Tests:AFPServer@*
-Q24: I want to be able to allow users to change their passwords?  How do
-     I enable this feature.  Every time I try I get an error that it was
-     unable to save the password.
-Q25: Can a mount a Mac volume on my unix machine?
-Q26: Can I run Samba and Netatalk together to access the same files?
-Q27: Files I create on my Samba shares are invisible on the mac side.
-Q27a: How can I set netatalk to hide some files from the Samba (or
-     unix) sides?
-Q28: Files I create on my netatalk shares are invisible on the PC side.
-Q28a: How can I set Samba to hide the netatalk specific files (e.g.
-     .AppleDouble).
-Q29: I compiled Samba with the --with-netatalk flag. What did that do?
-Q30: What about the differences in naming schemes, and legal/illegal
-     characters between Windows, Macs (and unix?)
-Q31: Where can I get the cnid-db (Berkely DB) software? (needed for
-     --with-did=cnid)
-Q32: What about security in Netatalk?
-
-
-
------------------------------------------------------------------------------
-
-
-Q1: Where can I get more information on Netatalk?
-
-A:  Netatalk's home page can be found at:
-
-      http://netatalk.sourceforge.net/
-
-    Netatalk is maintained at SourceForge. The Netatalk project page on
-    SourceForge is located at:
-
-      http://sourceforge.net/projects/netatalk/
-
-    There are (at least) three very active e-mail lists to which you can
-    subscribe. The first, netatalk-admins, is for usage and setup/compile
-    questions. Subscription information as well as an archive are available at:
-
-      http://lists.sourceforge.net/lists/listinfo/netatalk-admins
-
-    This can be very high volume, but usually a few messages a day.
-
-    Netatalk-devel list is more specific to coding and testing. The archive
-    and more information can found at:
-
-      http://lists.sourceforge.net/lists/listinfo/netatalk-devel
-
-    This list varies in volume, but is usually moderately active.
-
-    Netatalk-docs is specific to documentation. For more information see:
-
-      http://lists.sourceforge.net/mailman/listinfo/netatalk-docs
-    There are other netatalk information sites. Some of these are no
-    longer actively updated, some are site-specific, but still have
-    good information:
-
-      http://www.anders.com/projects/netatalk/
-      http://www.faredge.com.au/netatalk/index.html
-
-
-Q2: What is this I keep seeing about asun?
-
-A:  Before Netatalk moved to SourceForge, Adrian Sun (asun) had written
-    some patches to Netatalk which helped significantly with its usability,
-    especially using AppleShare IP. These patches are still provided by many
-    Unix vendors. All of these patches are included in the current SourceForge
-    versions.
-
-
-Q3: How do I get the most recent version of Netatalk?
-
-A:  Via Git from SourceForge.net. This is the actively maintained version
-    of Netatalk, changes are being made constantly, and therefore it is not
-    suitable for production environments. The netatalk at SourceForge is in
-    Beta, so keep that in mind.
-
-    Downloading the Git repository can be done quickly and easily.
-
-    Make sure you have Git installed. which git should produce a path to git.
-
-     $> which git
-     /usr/bin/git
-
-    If you don't have one make a source directory. cd to this directory.
-
-     $> mkdir /path/to/new/source/dir
-     $> cd /path/to/new/source/dir
-
-    Now get the source:
-
-     $> git clone git://netatalk.git.sourceforge.net/gitroot/netatalk/netatalk
-     Initialized empty Git repository in /path/to/new/source/dir/netatalk/.git/
-     remote: Counting objects: 2503, done.
-     ...
-
-    This will create a local directory called "netatalk" containing a complete
-    and fresh copy of the whole Netatalk source from the Git repository.
-
-    In order to keep your repository copy updated, occasionally run:
-
-     $> git pull
-
-    Now cd to the netatalk directory and run ./bootstrap. This will create the
-    configure script required in the next step.
-
-     $> ./bootstrap
-
-
-Q4: Can I get an almost current version of Netatalk without having to learn Git?
-
-A:  Yes.  Snapshots of the Git tree should be posted for the benefit of
-    those that don't want to / can't use Git. They are available at:
-
-      http://netatalk.git.sourceforge.net/git/gitweb-index.cgi
-    
-    You should be able to treat these images as you would a release.  Just
-    configure as you normally work, then run make (or gmake as the case may
-    be).  There is no need to run ./bootstrap on these images.
-
-
-Q4a: Is there an RPM, package, or tarball for my platform?
-
-A:  Perhaps. These vary in how often they're updated:
-
-    FreeBSD
-      port: /usr/ports/net/netatalk - maintained by Joe Clark
-    SuSE Linux
-      included in the distribution
-    OpenBSD
-      port: /usr/ports/net/netatalk/ - not actively maintained
-    Debian GNU/Linux
-      included in all current distributions
-    RedHat Linux
-      included in the distribution
-
-
-Q5: I'm having massive file deletion problems!
-
-Q6: I am having lots of file locking problems!
-
-Q7: I'm getting this message in my logs:
-    WARNING: DID conflict for ...  Are these the same file?
-
-A:  Compile with the --with-did=last flag set. This activates a different
-    method of calculating inodes in the software, and will hopefully fix some
-    of these problems. This code, along with the CNID code, was still being
-    worked out in Pre7. The cnid/bdb flags also go along with this:
-
-      --with-bdb=PATH         specify path to Berkeley DB installation
-      --with-did=[scheme]     set DID scheme (cnid,last) 
-    
-    (For more information on CNID, see the README.cnid file.)
-    
-    --with-did=last reverted things back to the old 1.4b2 directory ID
-    calculation algorithm.  This also solved the problem of the syslog
-    messages and the users complaining of file deletions.  It's also been
-    found that by disabling *BSD's SOFTUPDATES feature on Netatalk volumes (on
-    FreeBSD), multi-user interaction seemed to work better.  This was back in
-    a late 4.2-BETA, so it's not clear if this still holds true in 4.4-RELEASE
-    or not.
-
-
-Q8: I can't seem to use passwords longer than 8 characters for my Netatalk
-    accounts. How can I fix that? 
-
-Q9: I would like to use encrypted passwords to authenticate to the Netatalk
-    server. How do I do that?
-
-A:  Update to a newer version of AppleShare Client (I think the most
-    recent is 3.8.8). This allows longer passwords, and will allow you to
-    use encrypted passwords. Set which way you would like to authenticate
-    in either afpd.conf or netatalk.conf, depending on your setup.
-
-    For more information on the AppleShare Client from Apple, and which clients
-    are needed for which MacOS, see 
-
-      http://til.info.apple.com/techinfo.nsf/artnum/n60792?OpenDocument&software
-
-    (this site requires cookies, and a registration and sign-in).
-
-
-Q10: How can I set who has access to certain directories?
-    
-A:  You can certainly do this with your Unix permissions, but also explore the 
-    allow/deny/rwlist/rolist options in the AppleVolumes.default file:
-    
-      # allow/deny/rwlist/rolist format [syntax: allow:user1,@group]:
-      # user1,@group,user2  -> allows/denies access from listed users/groups
-      #                        rwlist/rolist control whether or not the
-      #                        volume is ro for those users.
-    
-    Also, some unices, specially FreeBSD, have other options:
-    (By Joe Clark)
-    
-    "What about file and directory permissions?  Since I didn't use the FORCE
-    UID/GID code, I decided to use a feature of FreeBSD called SUIDDIR. From
-    the LINT kernel config file:
-    
-    # If you are running a machine just as a fileserver for PC and MAC
-    # users, using SAMBA or Netatalk, you may consider setting this option
-    # and keeping all those users' directories on a filesystem that is
-    # mounted with the suiddir option. This gives new files the same
-    # ownership as the directory (similar to group). It's a security hole
-    # if you let these users run programs, so confine it to file-servers
-    # (but it'll save you lots of headaches in those cases). Root owned
-    # directories are exempt and X bits are cleared. The suid bit must be
-    # set on the directory as well; see chmod(1) PC owners can't see/set
-    # ownerships so they keep getting their toes trodden on. This saves
-    # you all the support calls as the filesystem it's used on will act as
-    # they expect: "It's my dir so it must be my file".
-    
-     FORCE UID/GID code, I decided to use a feature of FreeBSD called
-     SUIDDIR.  From the LINT kernel config file:
-    
-    # If you are running a machine just as a fileserver for PC and MAC
-    # users, using SAMBA or Netatalk, you may consider setting this option
-    # and keeping all those users' directories on a filesystem that is
-    # mounted with the suiddir option. This gives new files the same
-    # ownership as the directory (similar to group). It's a security hole
-    # if you let these users run programs, so confine it to file-servers
-    # (but it'll save you lots of headaches in those cases). Root owned
-    # directories are exempt and X bits are cleared. The suid bit must be
-    # set on the directory as well; see chmod(1) PC owners can't see/set
-    # ownerships so they keep getting their toes trodden on. This saves
-    # you all the support calls as the filesystem it's used on will act as
-    # they expect: "It's my dir so it must be my file".
-    
-    And the associated mount command:
-    
-    mount -o suiddir /dev/da2s1e /macvol/artfiles
-    
-    This was used on my dedicated Netatalk/Samba filesystems.  On
-    filesystems that were also used for interactive shell access, I chmod'd
-    my Netatalk shares 2770.  The reason for this is that I set up a UNIX
-    group for each department in the ad agency.  I had an art group, a media
-    group, an accounting group, and then, or course, a general staff group.
-    Each share was only allowed access by the group that needed to access
-    the share.  So, the Artfiles share allowed access only to the art group:
-    
-    /macvol/artfiles "Art Files" allow:@art
-    
-    And the others followed in kind.  Therefore, the 2770 mask allowed only
-    owners and people in the associated group access to read and write
-    files.  The leading 2 set the setgid bit so that all child files and
-    directories would retain the same group permissions.  I found this to
-    work well.
-    
-    This was used on my dedicated Netatalk/Samba filesystems.  On
-    filesystems that were also used for interactive shell access, I chmod'd
-    my Netatalk shares 2770.  The reason for this is that I set up a UNIX
-    group for each department in the ad agency.  I had an art group, a media
-    group, an accounting group, and then, or course, a general staff group.
-    Each share was only allowed access by the group that needed to access
-    the share.  So, the Artfiles share allowed access only to the art group:
-    
-    /macvol/artfiles "Art Files" allow:@art
-    
-    And the others followed in kind.  Therefore, the 2770 mask allowed only
-    owners and people in the associated group access to read and write
-    files.  The leading 2 set the setgid bit so that all child files and
-    directories would retain the same group permissions.  I found this to
-    work well."
-
-
-Q11: What are the .AppleDouble and .Parent directories which are created in
-     the Netatalk locations?
-
-A:  See the README.veto file in this directory.
-    
-    The .AppleDouble folders hold the resource fork information for the Mac
-    files, plus other attributes which are not normally stored by Unix. For
-    this reason, when you want to move files around in your Mac volumes, it's
-    a good idea to do it from the Mac side (as opposed to from the Unix side,
-    or Samba), unless you make absolutely sure you get the .AppleDouble
-    directories. These directories are often hidden from the Samba side, via
-    the veto files configuration.
-    
-    You can also set Netatalk to not create an .AppleDouble directory unless
-    it absolutely needs it, by setting the noadouble setting in
-    AppleVolumes.default.
-    
-    
-Q12: Hidden files - what's up with that?
-    
-A:  If you set the noadouble flag in AppleVolumes.default, you won't see
-    the .Apple* or .Parent directories on the Mac side. If you use the veto
-    files option in Samba, they may be hidden from the Windows side as well.
-    (More information in the Samba section, and in the README.veto file in
-    this directory.)
-
-
-Q13: I get a "socket: Invalid argument" error when trying to start Netatalk
-     under Linux. What is causing this?
-
-A:  The "appletalk" and "ipddp" kernel modules have to be installed under
-    linux for Netatalk to function. The appletalk module can be automatically
-    loaded by adding the line "alias net-pf-5 appletalk" to the
-    /etc/modules.conf file. Issuing the command "modprobe (module)" will
-    load the module for the current session.
-
-
-Q14: Netatalk works over AppleTalk, but my IP connections are refused, even
-     though I have enabled them in the configuration files.
-
-A:  If tcp_wrappers support is compiled into Netatalk, access has to be
-    granted in /etc/hosts.allow for Netatalk to successfully accept IP
-    connections. This can be done by the addition of the line:
-
-      afpd:  127. xxx.xxx.xxx. (whatever other subnets)    
-    
-
-Q15: I'm having Quark Express file locking problems, is there information on
-     that?
-
-A:  Yes, see the question regarding DID conflicts and the --enable-did= flag. 
-    Also, try using the --flock-locks flag. Enabling this code disabled the 
-    new byte locking feature. With FLOCK locks, the whole file would be locked. 
-    With byte locks, a byte range could be locked without locking the whole
-    file.
-
-
-Q16: I'm getting this error in Quark Express when trying to save a file to
-     the server: 'Error Type -50'
-
-A:  Turn off the document preview feature off in Quark.
-
-
-Q17: Does netatalk work with MacOS X?
-
-A:  Yes, but only the most recent versions, and it's still being finalized.
-    Versions prior to 1.5Pre7 did NOT work with OS X, although some really
-    early versions did (netatalk 1.4+asun?).
-
-
-Q18: I'm getting an 'Application for this document not found' error on MacOS X.
-
-Q19: I'm getting an 'Error Type -43' error on MacOS X.
-
-A:  Configure with --with-did=last. More info on this flag is given in the 
-    DID conflicts question.
-
-
-Q20: How do I get the directories that are created by Netatalk to have the
-     correct permissions by default?
-
-A:  Investigate the setgid bit on your Unix platform. It's a good idea to
-    set this on your shared directories, and your .AppleDouble directories.
-    From the mail archives: "Usually directories designated for use with
-    AppleShare have the setgid (g+s) bit set.  It forces inheritance of
-    permissions.  Without it, the .AppleDouble subdirectory can't be created
-    since the new folder doesn't necessarily have the same write privileges."
-
-    Information about the setgid bit can be found in Evi Nemeth's 
-    "Unix System Administration Handbook" (3rd. ed, chap 5.5, pg. 69):
-
-    "The bits with octal values 4000 and 2000 are the setuid and setgid bits.
-    These bits allow programs to access files and processes that would
-    otherwise be off-limits to the users that run them. [...] When set on a
-    directory, the setgid bit causes newly created files within the directory
-    to take on the group membership of the directory rather than the defualt
-    group of the user that created the file. This convention makes it easier
-    to share a directory of files among several users, as long as they all
-    belong to a common group. Check your system before relying on this
-    feature, since not all version of UNIX provide it. [...] This interpretation
-    of the setgid bit is unrelated to it's meaning when set on an executable
-    file, but there is never any ambiguity as to which meaning is
-    appropriate."
-    
-    NOTE: The setuid is usually discussed along with the setgid bit. The
-    setuid bit is VERY dangerous. If you set it on an executable, and the
-    executable is owned by root, anyone who runs that executable is root for
-    the duration of that executable's run, so a clever person can leverage
-    that into a full-scale compromise. The setgid bit also has other security
-    implications, so be careful where you set it.
-    
-    You set it by doing a chmod 2xxx, where xxx are the normal file permissions
-    (i.e. owner/group/other permissions).
-    
-
-Q21:  What does this error mean:
-     'afpd[#####]: setdirmode: chmod .AppleDouble Operation not permitted'
-
-A:  This can be due to a few things.
-
-    1) The setgid bit might not be set on either your directory, or on the
-    .AppleDouble directory. It has to be set recursively on the .AppleDouble
-    folder.
-    
-    2) You may not be member of the group set on the directory you're trying
-    to write to.
-    
-    3) This was a persistant bug in 1.5pre6 for awhile, upgrading might help.
-    
-    
-Q22: I'm having problems with the Trash folder: either when someone drags
-     files into it, the system wants them to delete them immediately, or files
-     get stuck in there and won't delete.
-    
-A:  chmod the Network Trash folder to 2775 (/home/public/Network Trash
-    Folder for instance).
-
-    As of 10/16/01, MacOS X trash didn't work properly with afps volumes. 
-    Apple is working on it.
-
-Q23: The daemons aren't starting, things aren't showing up in the Chooser,
-     and I get a message like this in the logs: afpd[####]: Can't register
-     Tests:AFPServer@*
-
-    This is sometimes a result of missing NIC information in the atalkd.conf
-    file. Put your network interface (something like le0, eth0, fxp0, lo0)
-    alone on a line in atalkd.conf, and reboot. When atalkd starts, it will
-    populate the file with a line such as:
-
-      le1 -seed -phase 2 -addr 66.6 -net 66-67 -zone "No Parking"
-
-    To find your network interface, run
-
-      % ifconfig -a | more
-
-    and see which interface has your IP address. Use that one.
-
-
-Q24: I want to be able to allow users to change their passwords.  How do
-     I enable this feature?  Every time I try I get an error that it was
-     unable to save the password.
-
-A:  Use -[no]setpassword in afpd.conf. This enables or disables the ability of
-    clients to change their passwords.
-
-
-Q25: Can a mount a Mac volume on my Unix machine?
-
-A:  Well, maybe. MacOS X obviously might be able to do this with NFS. 
-    Also, there is a program called afpfs which was designed to do this, 
-    but is not actively maintained and has been reportedly highly unstable. 
-    It should be available from:
-
-      http://www.panix.com/~dfoster/afpfs/
-
-Q26: Can I run Samba and Netatalk together to access the same files?
-
-A:  Sure. Lots of us do. But there are some concerns. Quite often it's 
-    useful, for instance, to hide files of one OS from the other. See
-    the AppleVolumes.default file in Netatalk, and investigate the veto
-    files option in Samba. (See the README.veto file.)
-
-    Also, when copying and moving files created on the Mac, it's better
-    to do that from the Mac, rather than from the Unix server or from
-    Samba. This is because the .AppleDouble folders hold the resource fork 
-    information for the Mac files, plus other attributes which are not 
-    normally stored by Unix.
-    
-    You can also set Netatalk to not create an .AppleDouble directory unless
-    it absolutely needs it, by setting the noadouble setting in
-    AppleVolumes.default.
-
-
-Q27: Files I create on my Samba shares are invisible on the Mac side.
-
-A:   Have you checked the AppleVolumes(.default? .sytem? I don't remember
-     which one hides files!) file?
-
-     How long are the file names? Names longer than 31 BYTES (not characters) 
-     are not visible on the Mac side. This is because some old MacOS's don't 
-     accept long names, and some Finders crash when they encounter them. 
-     Therefore Netatalk hides long filenames to prevent crashes. If you
-     prefer Netatalk to truncate the names, use the --with-mangling ./configure
-     option when compiling Netatalk.
-
-     The BYTES distiction is made because there exist doublebyte fonts too, 
-     which limit names to 15 chars.
-
-
-Q27a: How can I set Netatalk to hide some files created on the Samba 
-     (or Unix) sides?
-
-A:   AppleVolumes(.system or .default?) allows you to hide certain files.
-     This might be a good thing to set on, say, .cshrc, ssh keys, and
-     the like.
-
-
-Q28: Files I create on my Netatalk shares are invisible on the PC side.
-
-Q28a: How can I set Samba to hide the Netatalk specific files (e.g.
-     .AppleDouble).
-
-A:   Check your Samba veto files option in smb.conf. It's often useful
-     to hide files like .AppleDouble or the network trash folder here.
-
-     Does the mac file have a \ or / in it? Would this cause Samba to 
-     not see the file?
-
-
-Q29: I compiled Samba with the --with-netatalk flag. What did that do?
-
-A:   Nothing. Some code was written (by a Samba developer?), but as of 
-     Fall 2001, Samba doesn't utilize it.
-
-
-Q30: What about the differences in naming schemes, and legal/illegal
-     characters between Windows, Macs, and Unix?
-
-A:   Check out the documentation about the 'mswindows' flag in
-     AppleVolumes.default. For instance, having / or \ or : in a name is
-     especially bad, as they are path seperators on Unix, Windows, and MacOS, 
-     respectively). Educating the end user is important for this problem.
-
-
-Q31: Where can I get the cnid-db (Berkely DB) software? (needed for
-     --with-did=cnid)
-
-A:   First check to see if your Unix has a port or package. If not,
-     Berkeley DB is available at:
-
-       http://www.sleepycat.com/download.html
-
-Q32: What about security in Netatalk?
-
-A:   Most of the security for Netatalk must be derived from the
-     security of the Unix server on which it runs. Directory permissions,
-     valid users, firewalls, IP filters, file integrity checkers, etc.
-     are all part of the equation. That said, it is possible to configure
-     Netatalk to minimize access, and close potential security holes.
-
-     These two flags are especially important:
-
-       --with-tcp-wrappers: enable TCP wrappers support.
-
-         Enables Wietse Venema's network logger, also known as tcpd or
-         LOG_TCP. These programs log the client host name of incoming
-         telnet, ftp, rsh, rlogin, finger etc. requests. Security
-         options are: access control per host, domain and/or service;
-         detection of host name spoofing or host address spoofing;
-         booby traps to implement an early-warning system.  TCP
-         Wrappers can be gotten at:
-
-           ftp://ftp.porcupine.org/pub/security/
-
-         Note, if you use TCP Wrappers, it would be a good idea to set your
-         afpd.conf file to disable DDP, or accept connections only on TCP.
-         You can also configure afpd to only run on a certain port, which
-         you can then let through your IPFilter.
-     
-       --with-ssl-dirs=[PATH]: specify path to OpenSSL installation.
-
-         NOTE: This is dependent on the same directory layout as the
-         source distribution of OpenSSL. That is: include/ and
-        lib/ to be on the same level. Many .rpm formats do not
-         have their files laid out in this format.
-         The OpenSSL Project is a collaborative effort to develop a
-         robust, commercial-grade, full-featured, and Open Source
-         toolkit implementing the Secure Sockets Layer (SSL v2/v3)
-         and Transport Layer Security (TLS v1) protocols as well as a
-         full-strength general purpose cryptography library.
-         This is required to enable DHX login support, which
-         will encrypt all of the passwords being sent across the 
-         connection. (Some old Mac clients don't support this, check
-         this FAQ for the section on AppleShare clients.)
-         Check to see if your Unix has OpenSSL already, or
-         get everything at:
-
-           http://www.openssl.org/ 
-
-       --with-libgcrypt-dir=[PATH]: specify path to Libgcrypt installation.
-
-         NOTE: This is dependent on the same directory layout as the
-         source distribution of Libgcrypt. That is: include/ and
-        lib/ to be on the same level.
-         This is required to enable DHX2 login support, which
-         will encrypt all of the passwords being sent across the 
-         connection. (Some old Mac clients don't support this, check
-         this FAQ for the section on AppleShare clients.)
-         Check to see if your Unix has Libgcrypt already, or
-         get everything at:
-
-           http://directory.fsf.org/project/libgcrypt/
-
-    Be aware that on the volumes that are shared, some of the 
-    special folders (.AppleDesktop, "Network Trash Folder") get
-    assigned. A lot of these get created as world-writable (because that's
-    what the Mac clients are expecting them to be) which is often quite
-    undesirable from the Unix system administrator's point of view.
-    Documenting this behavior could be a somewhat daunting task, but
-    highly desirable.
-
-    Shares can be set to be read/write only by certain people and groups.
-
-    The Netatalk code has not been through a major code audit. However,
-    it's Open Source, so if you want to do said audit, contact the 
-    Netatalk maintainers (which can be done through the SourceForge site).
-
-    Has anyone tried to run Netatalk in a chroot jail? If so, please
-    share your experiences with the mailing lists.
index e242667ca577a75134659b2fb8cf95fb95e1a62c..05eaf8415abe36d5a1e997727004daf6c1d15da7 100644 (file)
@@ -1,10 +1,3 @@
 # Makefile.am for INSTALL/
 
-EXTRA_DIST = \
-       DEVELOPER \
-       FAQ \
-       README.documentation \
-       README.hidden-items \
-       README.ids \
-       README.AppleTalk \
-       README.ACLs
+EXTRA_DIST = DEVELOPER README.AppleTalk
diff --git a/doc/README.ACLs b/doc/README.ACLs
deleted file mode 100644 (file)
index 6c47725..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-
-                 ACLs - Konfiguration and Infos vor Developpers
-                 ==============================================
-
-ACL support for AFP is implemented with NFSv4 ACLs. Few filesystems and fewer OSes support
-these. At the time of implementation its only provided with ZFS on Solaris, Opensolaris and
-derived distributions.
-
-  Configuration
-  -------------
-
-In order to be able to support ACLs, the following things have to be configured:
-
-1. ZFS Volumes
-2. Authentication Domain
-3. Netatalk Volumes
-
-  1. ZFS Volumes:
-
-     You MUST configure two ACL parameters for any volume you want to use with Netatalk:
-
-        aclinherit = passthrough
-        aclmode = passthrough
-
-     For an explanation of what these parameters mean and how to apply them see, your hosts
-     ZFS documentation (e.g. man zfs).
-
-  2. Authentication Domain
-
-     Your server and the clients must be part of a security association where identity data
-     is coming from a common source. ACLs in Darwin are based on UUIDs and so is the ACL
-     specification in AFP 3.2. Therefor your source of identity data has to provide an
-     attribute for every user and group where a UUID is stored as a ASCII string.
-
-     In other words:
-      - you need an Open Directory Server or an LDAP server where you store UUIDs in some
-       attribute
-      - your clients must be configured to use this server 
-      - your server should be configured to use this server via nsswitch and PAM. This
-       however is not a strict requirement: 
-       if you create duplicates of every LDAP/OD user and group with identic attributes
-       (name, uid, gid) in your local data store (/etc/[passwd|group]) things will work
-
-             *  as long as user/group names/ids in the filesystem are equal  *
-             *         to their counterparts in the LDAP/OD datastore        *
-
-      - configure Netatalk via afp_ldap.conf so that Netatalk is able to retrieve the UUID
-       for users and groups via LDAP search queries
-
-  3. Netatalk Volumes
-
-     Finally you can add "options:acls" to your volume defintions to add ACL support.
-     In case your volume basedir doesn't grant read permissions via mode (like: 0700 root:adm)
-     but only via ACLs, you MUST add the "nostat" option to the volume defintion.
-
-  Implemantation Notes
-  --------------------
-
-Some implementation details that are buried in the code are worthwhile to be documented.
-
-1. Darwin ACEs vs NFSv4 ACEs
-2. .AppleDouble VFS integration
-
-  1. Darwin ACEs vs NFSv4 ACEs
-
-     Basically as far as implementing AFP support is concerned they're equivalent.
-     Subtleties arise at other places:
-
-     FPAccess
-
-       The AFP client frequently checks the (DARWIN_)ACE_DELETE_CHILD right. This is most
-       often not explicitly granted via an ACE. Therefor the client would get an no access
-        error. The client in turn then declares the object in question read only.
-       Thus we have to the check the mode for every directory and add ACE_DELETE_CHILD if
-       the requestor has write permissions.
-
-     FPGetFileDirParms
-
-       10.5 does not only use unix mode and FPAccess for permission check, but also OS 9
-       access bits from FPGetFileDirParms. Thus we have to adjust the Access Rights bitmap
-       user bits by including any ACL rigths.
-
-  2. .AppleDouble VFS integration
-
-     FPSetACL sets ACLs on files and dirs. Our implementation also sets the same ACL on the
-     .AppleDouble file for files and on the .AppleDouble dir itself for dirs.
-
-     Thereafter ACLs for created files is taken care of by ACLs own inheritance rules.
-
-     For dirs on the other hand whe have to make sure that any ACL the dir inherits is
-     copied verbatim to its .AppleDouble dir. 
-
-
-                                                                    January 2009, Frank Lahm
\ No newline at end of file
diff --git a/doc/README.documentation b/doc/README.documentation
deleted file mode 100644 (file)
index db58a07..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-                       ATTENTION
-
-The Netatalk documentation is now maintained in Docbook XML format.
-It is kept in the separate Git module 'netatalk-docs'.
diff --git a/doc/README.hidden-items b/doc/README.hidden-items
deleted file mode 100644 (file)
index f07b125..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-              Special folders created inside Netatalk shares
-
-(Originally by ali@gwc.org.uk 2001-10-20)
-(Amended by Sebastian Rittau 2001-11-03)
-(Amended by HAT 2010-03-30)
-
-Inside netatalk share points you will find several files and directories
-which are created automatically by the afpd process either for its own
-internal use or for the internal use of the MacOS.
-
-None of them should be directly visible in the Finder on the Mac. 
-
-Many of them have to be writeable in order for netatalk to function
-properly. This can present problems if users have shell access to the
-netatalk server. At the very least, users can "hide" files inside these
-writeable folders. At worst, a malicious user could confuse netatalk in a
-bad way. It is unlikely that a malicious user could cause loss of another
-user's data by exploiting permissions on these items.
-
-Below is what I hope to be a comprehensive list of these files and
-directories, their purpose, and a discussion of what Unix permissions
-should be set on them.
-
-Note that in general on Netatalk shares, all directories should have the
-setgid bit set. This forces any new files or folders created to have the
-same group as the folder they were created in. On some operating systems,
-notably FreeBSD, the group owner is always inherited from the parent
-directory, so the setgid bit is not necessary.
-
-
-.AppleDouble/
-
-This directory exists inside each folder on a Netatalk share. It contains
-meta information like the resource fork, or creator/type of each file in that
-folder. Its permissions should match those of its parent directory, i.e.
-anyone who has write access to the parent directory must have write access to
-the corresponding .AppleDouble directory.
-
-
-.AppleDouble/.Parent
-
-This file specifically contains meta information about the directory.
-
-
-.AppleDesktop/
-
-This directory exists under the top level of each share point. It contains
-the "desktop database" which is the method by which the MacOS associates a
-type/creator code with a particular application. Without it, documents
-will lose their application-specific icons and will have a generic icon
-instead. Double-clicking documents will also fail.
-
-To allow the desktop database to be maintained correctly, any user who is
-likely to copy an application on to the share must have write access to
-this directory and all directories below it. 
-
-
-Icon\r and .AppleDouble\Icon\r
-
-These files will exist in any folder, including the top level of a share,
-if it has a custom icon. Make them writeable to any user who should be
-allowed to change that custom icon; make them read-only if you don't want
-the custom icon to be changeable.
-
-
-.AppleDB/
-.AppleDBcnid.lock
-
-These will exist at the top level of each sharepoint on servers that run
-netatalk compiled with the new CNID DB code. Any user who has write access
-to any part of the share must have full write access to this directory /
-file and all the files within it otherwise the CNID DB code will not work
-properly.
-
-
-Network\ Trash\ Folder/
-
-This exists at the top level of each sharepoint. This is where files that
-are put in the Trash on clients go, until the Trash is emptied.
-
-The permissions of items in this directory are a pretty complicated
-subject, but basically you should make this directory and everything in it
-world-writeable if you want the Trash can to work properly. If you don't
-make it writeable then users will get a message "That item cannot be put
-in the Trash. Do you want to delete it immediately?" if they try to put
-something in the Trash.
-
-Unfortunately networked trash handling is broken in current versions of Mac
-OS X even if this directory is writeable. Apple is aware of this problem
-and is working on a solution.
-
-
-Temporary\ Items/
-.TemporaryItems/ (:2eTemporaryItems/)
-
-These folder may exist at the top level of a sharepoint. These folder is
-used by certain applications (Adobe PhotoShop among others) to store,
-well, temporary items. These programs may not work correctly if this
-folder is missing or not writeable, when a user tries to work on a
-document stored in that Netatalk share.
-
-
-TheFindByContentFolder/
-
-This folder is used by Sherlock 2 to store information use by its Find by
-Content feature. Make it writeable by users if you want to allow them to
-update the Find by Content index on a netatalk share. Otherwise, make it
-read-only.
-
-
-TheVolumeSettingsFolder/
-
-This folder is created at the top level of each share point. It
-always appears to be empty. It would be wise to set its permissions
-the same as the top level of the sharepoint.
-
-
-.DS_Store (:2eDS_Store)
-
-This file may appear in share points which have been accessed by a
-machine running Mac OS X. Its permissions should be set to match
-those of the enclosing directory.
-
-
-.FBCIndex (.FBCIndex)
-.FBCLockFolder/.FBCSemaphoreFile (:2eFBCLockFolder/:2eFBCSemaphoreFile)
-
-These are created to preserve retrieval information by Sherlock
-on Mac OS X 10.1. If these are removed, the next retrieval will slow.
-Mac OS X 10.2 and later do not use these.
diff --git a/doc/README.ids b/doc/README.ids
deleted file mode 100644 (file)
index a8539c3..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-File and Directory IDs explained.
-
-What are File and Directory ID's?
-
-On a mac the file system stores all files and directory information on each 
-volume in a big file called the catalogue file.  Inside the catalogue, all 
-files and directories are accessed using a key, central to which is a number, 
-called the ID.  In the case of a file its a file ID (FID) or for a directory, 
-a directory ID (DID).
-
-How many IDs can there be?
-
-The ID in a catalogue key is stored using 4 bytes, which means it can only 
-be between 0 and 4,294,967,295 (FF FF FF FF in hex).  However the first 16 
-IDs are reserved so you don't have quite that many.  Each ID is unique for 
-ever, on any given volume.  Once all 4 billion have been used, you cannot 
-create new files, so will need to reformat the volume to continue using it.
-
-Why are IDs so important?
-
-Most system calls relating to files inside a mac (either vi a network or a 
-hard disk) can refer to files or directories by ID.  This makes the ID a 
-powerful piece of information.  
-
-So whats the problem?
-
-The problem lies in file servers that don't use IDs.  The protocol used by
-macs to share files over a network is called the Apple Filing Protocol (AFP)
-and it requires the use of IDs.  So if you want to support AFP fully, any
-AFP server, must adopt its own system for storing and maintaining a link
-between each file or directory and its respective ID.  This is most critical
-when acessing directories.  
-
-So why does this matter on a non mac server like netatalk?
-
-The three big stumbling blocks that crop up with AFP servers that don't 
-fully support IDs are 1) aliases, 2) the trash can and 3) linked documents.
-
-Alias problems.
-
-An alias on a mac is quite special.  Rather than just storing the path to 
-the original file (like a unix symlink), it stores the ID to that file and 
-a special identifier for the volume (and the server it's on).  Ideally this 
-is great.  If the file moves or is renamed, the alias still works.  However 
-if either the file (or directory) ID changes, or the volume identifier 
-(or server identifer), then the alias will break.  The file it claims to 
-point to will claim to have been removed.  
-
-Trash can (accidentally deleted file) problems.
-
-The trash can has similar problems.  Files that have been moved to the trash
-are represented by their ID.  When you empty the trash all ID's listed are 
-deleted.  However if the ID of a file that was in the trash, is reallocated
-to an ordinary file, then when the trash is emptied that file will be deleted.
-
-Linked document problems.
-
-Finally linked documents: Linked documents are documents that contain hidden
-links to other documents.  Print setting and layout application (such as 
-Quark) use this technique.  Sometimes these documents contain IDs linking to
-their embeded documents.  These can break in the same way as aliases.  
-
-So how does netatalk approach the problem?
-
-!!! The following is outdated, please refer to the Manual instead !!!
-
-Netatalk has two different methods of allocating IDs: last and cnid.
-
-DID = last.
-
-This uses a running number to allocate IDs.  When an ID is allocated the 
-server remembers this by adding it to a table.  If an ID is referenced, then
-the server looks up on the table.  When the server is restarted, the table is
-lost.  This is the most simple method, but it is unreliable.  If you stick to
-the mac features which don't rely heavily on IDs it works fine.  If you try
-to use IDs much, things break.  
-
-DID = cnid. 
-
-The CNID scheme in Netatalk attempts to assign unique IDs to each file and
-directory, then keep those IDs persistent across mounts of the volume.  This
-way, cross-volume aliases will work, and users are less likely to encounter
-duplicate CNID errors.  Prior to Netatalk 1.6.0, the CNID calculation
-scheme was not persistent, and IDs were assigned based on the UNIX device and
-inode number of a given file or directory (see DID = last above).  This was 
-fine for the most part, but due to limitations, not all available CNIDs could 
-be used.  As well, these IDs could change independently from Netatalk, and 
-thus were not persistent.  As of Netatalk 1.6.0, the CNID scheme is now the 
-default. On top of that, Netatalk uses the Concurrent Datastore method to 
-avoid the need for database locking and transactions.
-
-As stated above, CNID requires Berkeley DB.  Currently, Netatalk supports
-BDB 4.1.25 and 4.2.52  The recommended version is 4.2.52 as that is the version
-on which most testing has been done.  
-
-CNID has seen many contributors over the years.  It was conceived by
-Adrian Sun <asun@zoology.washington.edu>.  His developer notes can be found
-libatalk/cnid/README file.  It was later picked up and modernized by Uwe Hees
-<uwe.hees@rz-online.de>.  Then, Joe Marcus Clarke <marcus@marcuscom.com>
-started fixing bugs and adding additional features.  The Concurrent
-Datastore support was subsequently added by Dan Wilga <dwilga@mtholyoke.edu>.
-The CNID code is currently maintained by Joe Marcus Clarke.
diff --git a/doc/TODO2.1 b/doc/TODO2.1
deleted file mode 100644 (file)
index 8296a10..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-NEW
-===
-Protocol level:
-* AFP 3.2
-* IPv6
-* Extended Attributes support
-* ACL support with ZFS
-* AppleTalk support in afpd and AppleTalk daemons (atalkd and papd) are disabled by default
-
-afpd:
-* default CNID backend is "dbd"
-* enable live debugging with SIGINT
-* afpd uses an in memory temporary DB if can't open the volume's database, but currently this only
-  works with the "cbd" or "tdb" backends not with the default "dbd".
-
-atalkd:
-* atalkd: workaround for broken Linux 2.6 AT kernel module:
-  Linux 2.6 sends broadcast queries to the first available socket which is in our case
-  the last configured one. atalkd now tries to find the right one.
-  Note: now a misconfigured or plugged router can broadcast a wrong route !
-
-Tools:
-* dbd: "dbd" CNID database and volume maintanance and intergrity check utility
-* apple_dump: dump AppleDouble files
-
-
-Upgrading from a 2.0 version
-============================
-2.1 and 2.0 filenames encoding and adouble headers are compatible thus
-it's possible to upgrade and downgrade between 2.0 and 2.1, you may have 
-to upgrade/downgrade (or delete) your .AppleDB folders in case the bdb versions differ.
-
-Requirements
-============
-BerkeleyDB 4.6
-
-configure
-=========
-new default:
-* sendfile is enable on linux
-
-new options
---disable-sendfile
---enable-nfsv4acls     NFSv4 ACL Support (only Solaris?)
-
-Webmin:
---with-webmin          path where webmin is installed [$PKGCONFDIR/webmin]
---with-webminuser              name for the webmin admin user
---with-webminversion           Webmin version to fetch from sf.net [1.490]
---with-webminpass      password for the webmin admin user
---with-webminport      TCP port for webmin
-
-removed options
---with-logfile
---with-cnid-dbd-txn     dbd always use transaction
---with-cnid-db3-backend  use dbd for transaction
---with-cnid-hash-backend never really work
-
-afpd.conf
-=========
-new defaults:
-* slp is disable by default
-* ddp is disable by default
-
-new options:
--slp                    advertise with SRVLOC
--hostname 
--volnamelen
--setuplog
--closevol
--ntdomain
--ntseparator
-
-removed options
--noslp
-
-AppleVolume.default
-===================
-new options:
-acl
-caseinsensitive                volume is case insensitive (JFS in OS2 mode)
-nocnidcache
-ea:sys|ad
-
-removed options:
-cachecnid
-
-Todo:
-=====
-- Clean up error messages, many messages should be move to the info/debug level
-  rather than error.
-- Are all options documented in the man pages/configuration files?
-- update dbd logic to others db.
-
-Best Practices:
-===============
-
-- use a separate user group for cnid_dbd daemons
-  cnid_metad -u afpd -g afpd
-
-- All CNID databases in the same directory
-
-  AppleVolumes.default
-
-    :DEFAUT: dbpath:/var/lib/afpd/$v
-
-  with /var/lib/afpd
-
-     drwxr-xr-x  afpd afpd 4096 2009-11-24 15:12 /var/lib/afpd
-
-  afpd or cnid_metad will create the right subdirectory ($v is replaced by the
-  volume name)
-
-MISC
-====
-Bonjour:
-with avahi you can add an afpd.service file in "/etc/avahi/services/".
-Drawback: AFP is advertised even if the server is down.
index 7ed56ed6612fc957a815944cf3eaf610e0727de5..6ad182a5693bd5b7278f9804bf0c0708e52282ca 100644 (file)
@@ -1,3 +1,7 @@
 # Makefile.am for etc/
 
-SUBDIRS = afpd cnid_dbd atalkd papd psf uams
+SUBDIRS = afpd cnid_dbd uams
+
+if USE_APPLETALK
+SUBDIRS += atalkd papd psf
+endif
index 54ae84fee31e9661e5a88c92f5ec8f8e5d151488..dd81a3f7e854021e52bb0ea8cb0c32e22ff53e1d 100644 (file)
@@ -5,33 +5,73 @@ pkgconfdir = @PKGCONFDIR@
 sbin_PROGRAMS = afpd
 noinst_PROGRAMS = hash
 
-afpd_SOURCES = unix.c ofork.c main.c switch.c auth.c volume.c directory.c \
-        file.c enumerate.c desktop.c filedir.c fork.c appl.c gettok.c \
-        mangle.c status.c afp_options.c afp_asp.c afp_dsi.c messages.c  \
-        afp_config.c nfsquota.c quota.c uam.c afs.c uid.c afp_util.c \
-       catsearch.c afprun.c hash.c extattrs.c
+afpd_SOURCES = \
+       afp_asp.c \
+       afp_avahi.c \
+       afp_config.c \
+       afp_dsi.c \
+       afp_options.c \
+       afp_util.c \
+       afp_zeroconf.c \
+       afprun.c \
+       afs.c \
+       appl.c \
+       auth.c \
+       catsearch.c \
+       desktop.c \
+       dircache.c \
+       directory.c \
+       enumerate.c \
+       extattrs.c \
+       file.c \
+       filedir.c \
+       fork.c \
+       gettok.c \
+       hash.c \
+       main.c \
+       mangle.c \
+       messages.c  \
+       nfsquota.c \
+       ofork.c \
+       quota.c \
+       status.c \
+       switch.c \
+       uam.c \
+       uid.c \
+       unix.c \
+       volume.c
 
-if USE_NFSv4_ACLS
+afpd_LDADD =  \
+       $(top_builddir)/libatalk/cnid/libcnid.la \
+       $(top_builddir)/libatalk/libatalk.la \
+       @LIBGCRYPT_LIBS@ @ZEROCONF_LIBS@ @QUOTA_LIBS@ @SLP_LIBS@ @WRAP_LIBS@ @LIBADD_DL@ @ACL_LIBS@ @PTHREAD_LIBS@
+
+afpd_LDFLAGS = -export-dynamic 
+
+afpd_CFLAGS = \
+       -I$(top_srcdir)/include \
+       -I$(top_srcdir)/sys \
+       @SLP_CFLAGS@ @ZEROCONF_CFLAGS@ \
+       -DAPPLCNAME \
+       -DSERVERTEXT=\"$(SERVERTEXT)/\" \
+       -D_PATH_AFPDDEFVOL=\"$(pkgconfdir)/AppleVolumes.default\" \
+       -D_PATH_AFPDSYSVOL=\"$(pkgconfdir)/AppleVolumes.system\" \
+       -D_PATH_AFPDPWFILE=\"$(pkgconfdir)/afppasswd\" \
+       -D_PATH_AFPDCONF=\"$(pkgconfdir)/afpd.conf\" \
+       -D_PATH_AFPDSIGCONF=\"$(pkgconfdir)/afp_signature.conf\" \
+       -D_PATH_AFPDUAMPATH=\"$(UAMS_PATH)/\" \
+       -D_PATH_ACL_LDAPCONF=\"$(pkgconfdir)/afp_ldap.conf\" \
+       -D_PATH_AFPDUUIDCONF=\"$(pkgconfdir)/afp_voluuid.conf\"
+
+if HAVE_ACLS
 afpd_SOURCES += acls.c
 endif
 
-afpd_LDADD =  $(top_builddir)/libatalk/cnid/libcnid.la $(top_builddir)/libatalk/libatalk.la @QUOTA_LIBS@ @SLP_LIBS@ @WRAP_LIBS@ @LIBADD_DL@
-afpd_LDFLAGS = -export-dynamic 
-afpd_CFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/sys \
-        @SLP_CFLAGS@ \
-        -D_PATH_AFPDDEFVOL=\"$(pkgconfdir)/AppleVolumes.default\" \
-        -D_PATH_AFPDSYSVOL=\"$(pkgconfdir)/AppleVolumes.system\" \
-        -D_PATH_AFPDPWFILE=\"$(pkgconfdir)/afppasswd\" \
-        -D_PATH_AFPDCONF=\"$(pkgconfdir)/afpd.conf\" \
-        -D_PATH_AFPDSIGCONF=\"$(pkgconfdir)/afp_signature.conf\" \
-        -D_PATH_AFPDUAMPATH=\"$(UAMS_PATH)/\" \
-        -D_PATH_ACL_LDAPCONF=\"$(pkgconfdir)/afp_ldap.conf\" \
-        -DAPPLCNAME \
-        -DSERVERTEXT=\"$(SERVERTEXT)/\"
 
 noinst_HEADERS = auth.h afp_config.h desktop.h directory.h file.h \
         filedir.h fork.h globals.h icon.h mangle.h misc.h status.h switch.h \
-        uam_auth.h uid.h unix.h volume.h hash.h acls.h acl_mappings.h extattrs.h
+        uam_auth.h uid.h unix.h volume.h hash.h acls.h acl_mappings.h extattrs.h \
+        dircache.h afp_zeroconf.h afp_avahi.h
 
 hash_SOURCES = hash.c
 hash_CFLAGS = -DKAZLIB_TEST_MAIN -I$(top_srcdir)/include
index d1ddbc13344bc45cd8d1fe4c54685b1dbbcd5601..b7c4b6d266fdb9863005c61dc889cd66785453e7 100644 (file)
@@ -1,5 +1,4 @@
 /*
-   $Id: acl_mappings.h,v 1.1 2009-02-02 11:55:00 franklahm Exp $
    Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
 
    This program is free software; you can redistribute it and/or modify
 #ifndef ACL_MAPPINGS
 #define ACL_MAPPINGS
 
+#ifdef HAVE_SOLARIS_ACLS
 #include <sys/acl.h>
+#endif
+
 #include "acls.h"
 
 /* 
  * Stuff for mapping between ACL implementations
  */
 
+/* Solaris 10u8 still hasn't got ACE_INHERITED_ACE */
+#ifndef ACE_INHERITED_ACE
+#define ACE_INHERITED_ACE 0x0080
+#endif
+
 struct ace_rights_map {
     u_int32_t from;
     u_int32_t to;
 };
 
+#ifdef HAVE_SOLARIS_ACLS
 struct ace_rights_map nfsv4_to_darwin_rights[] = {
     {ACE_READ_DATA,         DARWIN_ACE_READ_DATA},
     {ACE_WRITE_DATA,        DARWIN_ACE_WRITE_DATA},
@@ -89,5 +97,6 @@ struct darwin_to_nfsv4_flags_map darwin_to_nfsv4_flags[] = {
     {DARWIN_ACE_FLAGS_INHERITED,         ACE_INHERITED_ACE},
     {0,0}
 };
+#endif /* HAVE_SOLARIS_ACLS */
 
 #endif /* ACL_MAPPINGS */
index fd6f3ab4cea3f1114a9061668f4636dccad255df..43e91042f10e0ee3850a2d3cc312b7b51ee7a038 100644 (file)
@@ -1,6 +1,5 @@
 /*
-  $Id: acls.c,v 1.9 2010-03-08 19:49:59 franklahm Exp $
-  Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
+  Copyright (c) 2008, 2009, 2010 Frank Lahm <franklahm@gmail.com>
 
   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
 #include <grp.h>
 #include <pwd.h>
 #include <errno.h>
+#ifdef HAVE_SOLARIS_ACLS
 #include <sys/acl.h>
+#endif
+#ifdef HAVE_POSIX_ACLS
+#include <sys/acl.h>
+#include <acl/libacl.h>
+#endif
 
+#include <atalk/errchk.h>
 #include <atalk/adouble.h>
 #include <atalk/vfs.h>
 #include <atalk/afp.h>
 #include <atalk/logger.h>
 #include <atalk/uuid.h>
 #include <atalk/acl.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
 
 #include "directory.h"
 #include "desktop.h"
 #include "volume.h"
 #include "fork.h"
-
+#include "unix.h"
 #include "acls.h"
 #include "acl_mappings.h"
+#include "auth.h"
 
 /* for map_acl() */
-#define SOLARIS_2_DARWIN 1
-#define DARWIN_2_SOLARIS 2
+#define SOLARIS_2_DARWIN       1
+#define DARWIN_2_SOLARIS       2
+#define POSIX_DEFAULT_2_DARWIN 3
+#define POSIX_ACCESS_2_DARWIN  4
+#define DARWIN_2_POSIX_DEFAULT 5
+#define DARWIN_2_POSIX_ACCESS  6
+
+#define MAP_MASK               31
+#define IS_DIR                 32
 
 /********************************************************
- * Basic and helper funcs
+ * Solaris funcs
  ********************************************************/
 
-/*
-  Takes a users name, uid and primary gid and checks if user is member of any group
-  Returns -1 if no or error, 0 if yes
-*/
-static int check_group(char *name, uid_t uid, gid_t pgid, gid_t path_gid)
+#ifdef HAVE_SOLARIS_ACLS
+
+/*! 
+ * Compile access rights for a user to one file-system object
+ *
+ * This combines combines all access rights for a user to one fs-object and
+ * returns the result as a Darwin allowed rights ACE.
+ * This must honor trivial ACEs which are a mode_t mapping.
+ *
+ * @param path           (r) path to filesystem object
+ * @param sb             (r) struct stat of path
+ * @param result         (w) resulting Darwin allow ACE
+ *
+ * @returns                  0 or -1 on error
+ */
+static int solaris_acl_rights(const char *path,
+                              const struct stat *sb,
+                              uint32_t *result)
 {
-    int i;
-    struct group *grp;
+    EC_INIT;
+    int      i, ace_count, checkgroup;
+    ace_t    *aces = NULL;
+    uid_t    who;
+    uint16_t flags, type;
+    uint32_t rights, allowed_rights = 0, denied_rights = 0, darwin_rights;
 
-    if (pgid == path_gid)
-        return 0;
+    /* Get ACL from file/dir */
+    EC_NEG1_LOG(ace_count = get_nfsv4_acl(path, &aces));
 
-    grp = getgrgid(path_gid);
-    if (!grp)
-        return -1;
+    if (ace_count == 0)
+        goto EC_CLEANUP;
 
+    /* Now check requested rights */
     i = 0;
-    while (grp->gr_mem[i] != NULL) {
-        if ( (strcmp(grp->gr_mem[i], name)) == 0 ) {
-            LOG(log_debug, logtype_afpd, "check_group: requested user:%s is member of: %s", name, grp->gr_name);
-            return 0;
-        }
-        i++;
-    }
-
-    return -1;
-}
-
-/*
-  Remove any trivial ACE "in-place". Returns no of non-trivial ACEs
-*/
-static int strip_trivial_aces(ace_t **saces, int sacecount)
-{
-    int i,j;
-    int nontrivaces = 0;
-    ace_t *aces = *saces;
-    ace_t *new_aces;
-
-    /* Count non-trivial ACEs */
-    for (i=0; i < sacecount; ) {
-        if ( ! (aces[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)))
-            nontrivaces++;
-        i++;
-    }
-    /* malloc buffer for new ACL */
-    if ((new_aces = malloc(nontrivaces * sizeof(ace_t))) == NULL) {
-        LOG(log_error, logtype_afpd, "strip_trivial_aces: malloc %s", strerror(errno));
-        return -1;
-    }
-
-    /* Copy non-trivial ACEs */
-    for (i=0, j=0; i < sacecount; ) {
-        if ( ! (aces[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE))) {
-            memcpy(&new_aces[j], &aces[i], sizeof(ace_t));
-            j++;
-        }
-        i++;
-    }
-
-    free(aces);
-    *saces = new_aces;
-
-    LOG(log_debug7, logtype_afpd, "strip_trivial_aces: non-trivial ACEs: %d", nontrivaces);
-
-    return nontrivaces;
-}
+    do { /* Loop through ACEs */
+        who = aces[i].a_who;
+        flags = aces[i].a_flags;
+        type = aces[i].a_type;
+        rights = aces[i].a_access_mask;
 
-/*
-  Remove non-trivial ACEs "in-place". Returns no of trivial ACEs.
-*/
-static int strip_nontrivial_aces(ace_t **saces, int sacecount)
-{
-    int i,j;
-    int trivaces = 0;
-    ace_t *aces = *saces;
-    ace_t *new_aces;
-
-    /* Count trivial ACEs */
-    for (i=0; i < sacecount; ) {
-        if ((aces[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)))
-            trivaces++;
-        i++;
-    }
-    /* malloc buffer for new ACL */
-    if ((new_aces = malloc(trivaces * sizeof(ace_t))) == NULL) {
-        LOG(log_error, logtype_afpd, "strip_nontrivial_aces: malloc %s", strerror(errno));
-        return -1;
-    }
+        if (flags & ACE_INHERIT_ONLY_ACE)
+            continue;
 
-    /* Copy trivial ACEs */
-    for (i=0, j=0; i < sacecount; ) {
-        if ((aces[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE))) {
-            memcpy(&new_aces[j], &aces[i], sizeof(ace_t));
-            j++;
+        /* Now the tricky part: decide if ACE effects our user. I'll explain:
+           if its a dedicated (non trivial) ACE for the user
+           OR
+           if its a ACE for a group we're member of
+           OR
+           if its a trivial ACE_OWNER ACE and requested UUID is the owner
+           OR
+           if its a trivial ACE_GROUP ACE and requested UUID is group
+           OR
+           if its a trivial ACE_EVERYONE ACE
+           THEN
+           process ACE */
+        if (((who == uuid) && !(flags & (ACE_TRIVIAL|ACE_IDENTIFIER_GROUP)))
+            ||
+            ((flags & ACE_IDENTIFIER_GROUP) && !(flags & ACE_GROUP) && gmem(who))
+            ||
+            ((flags & ACE_OWNER) && (uuid == sb->st_uid))
+            ||
+            ((flags & ACE_GROUP) && !(uuid == sb->st_uid) && gmem(sb->st_gid))
+            ||
+            (flags & ACE_EVERYONE && !(uuid == sb->st_uid) && !gmem(sb->st_gid))
+            ) {
+            /* Found an applicable ACE */
+            if (type == ACE_ACCESS_ALLOWED_ACE_TYPE)
+                allowed_rights |= rights;
+            else if (type == ACE_ACCESS_DENIED_ACE_TYPE)
+                /* Only or to denied rights if not previously allowed !! */
+                denied_rights |= ((!allowed_rights) & rights);
         }
-        i++;
-    }
-    /* Free old ACEs */
-    free(aces);
-    *saces = new_aces;
+    } while (++i < ace_count);
 
-    LOG(log_debug7, logtype_afpd, "strip_nontrivial_aces: trivial ACEs: %d", trivaces);
 
-    return trivaces;
-}
+    /* Darwin likes to ask for "delete_child" on dir,
+       "write_data" is actually the same, so we add that for dirs */
+    if (S_ISDIR(sb->st_mode) && (allowed_rights & ACE_WRITE_DATA))
+        allowed_rights |= ACE_DELETE_CHILD;
 
-/*
-  Concatenate ACEs
-*/
-static ace_t *concat_aces(ace_t *aces1, int ace1count, ace_t *aces2, int ace2count)
-{
-    ace_t *new_aces;
-    int i, j;
+    /* Remove denied from allowed rights */
+    allowed_rights &= ~denied_rights;
 
-    /* malloc buffer for new ACL */
-    if ((new_aces = malloc((ace1count + ace2count) * sizeof(ace_t))) == NULL) {
-        LOG(log_error, logtype_afpd, "combine_aces: malloc %s", strerror(errno));
-        return NULL;
+    /* map rights */
+    darwin_rights = 0;
+    for (i=0; nfsv4_to_darwin_rights[i].from != 0; i++) {
+        if (allowed_rights & nfsv4_to_darwin_rights[i].from)
+            darwin_rights |= nfsv4_to_darwin_rights[i].to;
     }
 
-    /* Copy ACEs from buf1 */
-    for (i=0; i < ace1count; ) {
-        memcpy(&new_aces[i], &aces1[i], sizeof(ace_t));
-        i++;
-    }
+    *result |= darwin_rights;
 
-    j = i;
+EC_CLEANUP:
+    if (aces) free(aces);
 
-    /* Copy ACEs from buf2 */
-    for (i=0; i < ace2count; ) {
-        memcpy(&new_aces[j], &aces2[i], sizeof(ace_t));
-        i++;
-        j++;
-    }
-    return new_aces;
+    EC_EXIT;
 }
 
-
 /*
   Maps ACE array from Solaris to Darwin. Darwin ACEs are stored in network byte order.
   Return numer of mapped ACEs or -1 on error.
   All errors while mapping (e.g. getting UUIDs from LDAP) are fatal.
 */
-static int map_aces_solaris_to_darwin(ace_t *aces, darwin_ace_t *darwin_aces, int ace_count)
+static int map_aces_solaris_to_darwin(const ace_t *aces,
+                                      darwin_ace_t *darwin_aces,
+                                      int ace_count)
 {
+    EC_INIT;
     int i, count = 0;
     uint32_t flags;
     uint32_t rights;
     struct passwd *pwd = NULL;
     struct group *grp = NULL;
 
-    LOG(log_debug7, logtype_afpd, "map_aces_solaris_to_darwin: parsing %d ACES", ace_count);
+    LOG(log_maxdebug, logtype_afpd, "map_aces_solaris_to_darwin: parsing %d ACES", ace_count);
 
     while(ace_count--) {
-        LOG(log_debug7, logtype_afpd, "map_aces_solaris_to_darwin: parsing ACE No. %d", ace_count + 1);
+        LOG(log_maxdebug, logtype_afpd, "ACE No. %d", ace_count + 1);
         /* if its a ACE resulting from nfsv4 mode mapping, discard it */
         if (aces->a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)) {
-            LOG(log_debug, logtype_afpd, "map_aces_solaris_to_darwin: trivial ACE");
+            LOG(log_debug, logtype_afpd, "trivial ACE");
             aces++;
             continue;
         }
 
-        if ( ! (aces->a_flags & ACE_IDENTIFIER_GROUP) ) {
-            /* its a user ace */
-            LOG(log_debug, logtype_afpd, "map_aces_solaris_to_darwin: found user ACE with uid: %d", aces->a_who);
-            pwd = getpwuid(aces->a_who);
-            if (!pwd) {
-                LOG(log_error, logtype_afpd, "map_aces_solaris_to_darwin: getpwuid error: %s", strerror(errno));
-                return -1;
-            }
-            LOG(log_debug, logtype_afpd, "map_aces_solaris_to_darwin: uid: %d -> name: %s", aces->a_who, pwd->pw_name);
-            if ( (getuuidfromname(pwd->pw_name, UUID_USER, darwin_aces->darwin_ace_uuid)) != 0) {
-                LOG(log_error, logtype_afpd, "map_aces_solaris_to_darwin: getuuidfromname error");
-                return -1;
-            }
-        } else {
-            /* its a group ace */
-            LOG(log_debug, logtype_afpd, "map_aces_solaris_to_darwin: found group ACE with gid: %d", aces->a_who);
-            grp = getgrgid(aces->a_who);
-            if (!grp) {
-                LOG(log_error, logtype_afpd, "map_aces_solaris_to_darwin: getgrgid error: %s", strerror(errno));
-                return -1;
-            }
-            LOG(log_debug, logtype_afpd, "map_aces_solaris_to_darwin: gid: %d -> name: %s", aces->a_who, grp->gr_name);
-            if ( (getuuidfromname(grp->gr_name, UUID_GROUP, darwin_aces->darwin_ace_uuid)) != 0) {
-                LOG(log_error, logtype_afpd, "map_aces_solaris_to_darwin: getuuidfromname error");
-                return -1;
-            }
+        if ( ! (aces->a_flags & ACE_IDENTIFIER_GROUP) ) { /* user ace */
+            LOG(log_debug, logtype_afpd, "uid: %d", aces->a_who);
+            EC_NULL_LOG(pwd = getpwuid(aces->a_who));
+            LOG(log_debug, logtype_afpd, "uid: %d -> name: %s", aces->a_who, pwd->pw_name);
+            EC_ZERO_LOG(getuuidfromname(pwd->pw_name,
+                                        UUID_USER,
+                                        darwin_aces->darwin_ace_uuid));
+        } else { /* group ace */
+            LOG(log_debug, logtype_afpd, "gid: %d", aces->a_who);
+            EC_NULL_LOG(grp = getgrgid(aces->a_who));
+            LOG(log_debug, logtype_afpd, "gid: %d -> name: %s", aces->a_who, grp->gr_name);
+            EC_ZERO_LOG(getuuidfromname(grp->gr_name,
+                                        UUID_GROUP,
+                                        darwin_aces->darwin_ace_uuid));
         }
 
         /* map flags */
@@ -270,6 +237,8 @@ static int map_aces_solaris_to_darwin(ace_t *aces, darwin_ace_t *darwin_aces, in
     }
 
     return count;
+EC_CLEANUP:
+    EC_EXIT;
 }
 
 /*
@@ -277,14 +246,17 @@ static int map_aces_solaris_to_darwin(ace_t *aces, darwin_ace_t *darwin_aces, in
   Return numer of mapped ACEs or -1 on error.
   All errors while mapping (e.g. getting UUIDs from LDAP) are fatal.
 */
-int map_aces_darwin_to_solaris(darwin_ace_t *darwin_aces, ace_t *nfsv4_aces, int ace_count)
+static int map_aces_darwin_to_solaris(darwin_ace_t *darwin_aces,
+                                      ace_t *nfsv4_aces,
+                                      int ace_count)
 {
+    EC_INIT;
     int i, mapped_aces = 0;
     uint32_t darwin_ace_flags;
     uint32_t darwin_ace_rights;
     uint16_t nfsv4_ace_flags;
     uint32_t nfsv4_ace_rights;
-    char *name;
+    char *name = NULL;
     uuidtype_t uuidtype;
     struct passwd *pwd;
     struct group *grp;
@@ -294,23 +266,22 @@ int map_aces_darwin_to_solaris(darwin_ace_t *darwin_aces, ace_t *nfsv4_aces, int
         nfsv4_ace_rights = 0;
 
         /* uid/gid first */
-        if ( (getnamefromuuid(darwin_aces->darwin_ace_uuid, &name, &uuidtype)) != 0)
-            return -1;
-        if (uuidtype == UUID_USER) {
-            pwd = getpwnam(name);
-            if (!pwd) {
-                LOG(log_error, logtype_afpd, "map_aces_darwin_to_solaris: getpwnam: %s", strerror(errno));
-                return -1;
-            }
+        EC_ZERO(getnamefromuuid(darwin_aces->darwin_ace_uuid, &name, &uuidtype));
+        switch (uuidtype) {
+        case UUID_LOCAL:
+            free(name);
+            name = NULL;
+            darwin_aces++;
+            continue;
+        case UUID_USER:
+            EC_NULL_LOG(pwd = getpwnam(name));
             nfsv4_aces->a_who = pwd->pw_uid;
-        } else { /* hopefully UUID_GROUP*/
-            grp = getgrnam(name);
-            if (!grp) {
-                LOG(log_error, logtype_afpd, "map_aces_darwin_to_solaris: getgrnam: %s", strerror(errno));
-                return -1;
-            }
+            break;
+        case UUID_GROUP:
+            EC_NULL_LOG(grp = getgrnam(name));
             nfsv4_aces->a_who = (uid_t)(grp->gr_gid);
             nfsv4_ace_flags |= ACE_IDENTIFIER_GROUP;
+            break;
         }
         free(name);
         name = NULL;
@@ -350,32 +321,489 @@ int map_aces_darwin_to_solaris(darwin_ace_t *darwin_aces, ace_t *nfsv4_aces, int
     }
 
     return mapped_aces;
+EC_CLEANUP:
+    if (name)
+        free(name);
+    EC_EXIT;
 }
+#endif /* HAVE_SOLARIS_ACLS */
 
 /********************************************************
- * 2nd level funcs
+ * POSIX 1e funcs
  ********************************************************/
 
-/*  Map between ACL styles (SOLARIS_2_DARWIN, DARWIN_2_SOLARIS).
-    Reads from 'aces' buffer, writes to 'rbuf' buffer.
-    Caller must provide buffer.
-    Darwin ACEs are read and written in network byte order.
-    Needs to know how many ACEs are in the ACL (ace_count). Ignores trivial ACEs.
-    Return no of mapped ACEs or -1 on error. */
-static int map_acl(int type, ace_t *nfsv4_aces, darwin_ace_t *buf, int ace_count)
+#ifdef HAVE_POSIX_ACLS
+
+static uint32_t posix_permset_to_darwin_rights(acl_entry_t e, int is_dir)
+{
+    EC_INIT;
+    uint32_t rights = 0;
+    acl_permset_t permset;
+
+    EC_ZERO_LOG(acl_get_permset(e, &permset));
+
+    if (acl_get_perm(permset, ACL_READ))
+        rights = DARWIN_ACE_READ_DATA
+            | DARWIN_ACE_READ_EXTATTRIBUTES
+            | DARWIN_ACE_READ_ATTRIBUTES
+            | DARWIN_ACE_READ_SECURITY;
+    if (acl_get_perm(permset, ACL_WRITE)) {
+        rights |= DARWIN_ACE_WRITE_DATA
+            | DARWIN_ACE_APPEND_DATA
+            | DARWIN_ACE_WRITE_EXTATTRIBUTES
+            | DARWIN_ACE_WRITE_ATTRIBUTES;
+        if (is_dir)
+            rights |= DARWIN_ACE_DELETE_CHILD;
+    }
+    if (acl_get_perm(permset, ACL_EXECUTE))
+        rights |= DARWIN_ACE_EXECUTE;
+
+EC_CLEANUP:
+    LOG(log_maxdebug, logtype_afpd, "mapped rights: 0x%08x", rights);
+    return rights;
+}
+
+/*! 
+ * Compile access rights for a user to one file-system object
+ *
+ * This combines combines all access rights for a user to one fs-object and
+ * returns the result as a Darwin allowed rights ACE.
+ * This must honor trivial ACEs which are a mode_t mapping.
+ *
+ * @param path           (r) path to filesystem object
+ * @param sb             (r) struct stat of path
+ * @param result         (rw) resulting Darwin allow ACE
+ *
+ * @returns                  0 or -1 on error
+ */
+static int posix_acl_rights(const char *path,
+                            const struct stat *sb,
+                            uint32_t *result)
+{
+    EC_INIT;
+    int havemask = 0;
+    int entry_id = ACL_FIRST_ENTRY;
+    uint32_t rights = 0, maskrights = 0;
+    uid_t *uid = NULL;
+    gid_t *gid = NULL;
+    acl_t acl = NULL;
+    acl_entry_t e;
+    acl_tag_t tag;
+
+    EC_NULL_LOG(acl = acl_get_file(path, ACL_TYPE_ACCESS));
+
+    /* itereate through all ACEs to get the mask */
+    while (!havemask && acl_get_entry(acl, entry_id, &e) == 1) {
+        entry_id = ACL_NEXT_ENTRY;
+        EC_ZERO_LOG(acl_get_tag_type(e, &tag));
+        switch (tag) {
+        case ACL_MASK:
+            maskrights = posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
+            LOG(log_maxdebug, logtype_afpd, "maskrights: 0x%08x", maskrights);
+            havemask = 1;
+            break;
+        default:
+            continue;
+        }
+    }
+
+    /* itereate through all ACEs */
+    entry_id = ACL_FIRST_ENTRY;
+    while (acl_get_entry(acl, entry_id, &e) == 1) {
+        entry_id = ACL_NEXT_ENTRY;
+        EC_ZERO_LOG(acl_get_tag_type(e, &tag));
+        switch (tag) {
+        case ACL_USER:
+            EC_NULL_LOG(uid = (uid_t *)acl_get_qualifier(e));
+            if (*uid == uuid) {
+                LOG(log_maxdebug, logtype_afpd, "ACL_USER: %u", *uid);
+                rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
+            }
+            acl_free(uid);
+            uid = NULL;
+            break;
+        case ACL_USER_OBJ:
+            if (sb->st_uid == uuid) {
+                LOG(log_maxdebug, logtype_afpd, "ACL_USER_OBJ: %u", sb->st_uid);
+                rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
+            }
+            break;
+        case ACL_GROUP:
+            EC_NULL_LOG(gid = (gid_t *)acl_get_qualifier(e));
+            if (gmem(*gid)) {
+                LOG(log_maxdebug, logtype_afpd, "ACL_GROUP: %u", *gid);
+                rights |= (posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode)) & maskrights);
+            }
+            acl_free(gid);
+            gid = NULL;
+            break;
+        case ACL_GROUP_OBJ:
+            if (!(sb->st_uid == uuid) && gmem(sb->st_gid)) {
+                LOG(log_maxdebug, logtype_afpd, "ACL_GROUP_OBJ: %u", sb->st_gid);
+                rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));            
+            }
+            break;
+        case ACL_OTHER:
+            if (!(sb->st_uid == uuid) && !gmem(sb->st_gid)) {
+                LOG(log_maxdebug, logtype_afpd, "ACL_OTHER");
+                rights |= posix_permset_to_darwin_rights(e, S_ISDIR(sb->st_mode));
+            }
+            break;
+        default:
+            continue;
+        }
+    } /* while */
+
+    *result |= rights;
+
+EC_CLEANUP:
+    if (acl) acl_free(acl);
+    if (uid) acl_free(uid);
+    if (gid) acl_free(gid);
+    EC_EXIT;
+}
+
+/*!
+ * Add entries of one acl to another acl
+ *
+ * @param aclp   (rw) destination acl where new aces will be added
+ * @param acl    (r)  source acl where aces will be copied from
+ *
+ * @returns 0 on success, -1 on error
+ */
+static int acl_add_acl(acl_t *aclp, const acl_t acl)
+{
+    EC_INIT;
+    int id;
+    acl_entry_t se, de;
+
+    for (id = ACL_FIRST_ENTRY; acl_get_entry(acl, id, &se) == 1; id = ACL_NEXT_ENTRY) {
+        EC_ZERO_LOG_ERR(acl_create_entry(aclp, &de), AFPERR_MISC);
+        EC_ZERO_LOG_ERR(acl_copy_entry(de, se), AFPERR_MISC);
+    }
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+
+/*!
+ * Map Darwin ACE rights to POSIX 1e perm
+ *
+ * We can only map few rights:
+ *   DARWIN_ACE_READ_DATA                    -> ACL_READ
+ *   DARWIN_ACE_WRITE_DATA                   -> ACL_WRITE
+ *   DARWIN_ACE_DELETE_CHILD & (is_dir == 1) -> ACL_WRITE
+ *   DARWIN_ACE_EXECUTE                      -> ACL_EXECUTE
+ *
+ * @param entry             (rw) result of the mapping
+ * @param is_dir            (r) 1 for dirs, 0 for files
+ *
+ * @returns mapping result as acl_perm_t, -1 on error
+ */
+static acl_perm_t map_darwin_right_to_posix_permset(uint32_t darwin_ace_rights, int is_dir)
+{
+    acl_perm_t perm = 0;
+
+    if (darwin_ace_rights & DARWIN_ACE_READ_DATA)
+        perm |= ACL_READ;
+
+    if (darwin_ace_rights & (DARWIN_ACE_WRITE_DATA | (DARWIN_ACE_DELETE_CHILD & is_dir)))
+        perm |= ACL_WRITE;
+
+    if (darwin_ace_rights & DARWIN_ACE_EXECUTE)
+        perm |= ACL_EXECUTE;
+
+    return perm;
+}
+
+/*!
+ * Add a ACL_USER or ACL_GROUP permission to an ACL, extending existing ACEs
+ *
+ * Add a permission of "type" for user or group "id" to an ACL. Scan the ACL
+ * for existing permissions for this type/id, if there is one add the perm,
+ * otherwise create a new ACL entry.
+ * perm can be or'ed ACL_READ, ACL_WRITE and ACL_EXECUTE.  
+ *
+ * @param aclp     (rw) pointer to ACL
+ * @param type     (r)  acl_tag_t of ACL_USER or ACL_GROUP
+ * @param id       (r)  uid_t uid for ACL_USER, or gid casted to uid_t for ACL_GROUP
+ * @param perm     (r)  acl_perm_t permissions to add
+ *
+ * @returns 0 on success, -1 on failure
+ */
+static int posix_acl_add_perm(acl_t *aclp, acl_tag_t type, uid_t id, acl_perm_t perm)
+{
+    EC_INIT;
+    uid_t *eid = NULL;
+    acl_entry_t e;
+    acl_tag_t tag;
+    int entry_id = ACL_FIRST_ENTRY;
+    acl_permset_t permset;
+
+    int found = 0;
+    for ( ; (! found) && acl_get_entry(*aclp, entry_id, &e) == 1; entry_id = ACL_NEXT_ENTRY) {
+        EC_ZERO_LOG(acl_get_tag_type(e, &tag));
+        if (tag != ACL_USER && tag != ACL_GROUP)
+            continue;
+        EC_NULL_LOG(eid = (uid_t *)acl_get_qualifier(e));
+        if ((*eid == id) && (type == tag)) {
+            /* found an ACE for this type/id */
+            found = 1;
+            EC_ZERO_LOG(acl_get_permset(e, &permset));
+            EC_ZERO_LOG(acl_add_perm(permset, perm));
+        }
+
+        acl_free(eid);
+        eid = NULL;
+    }
+
+    if ( ! found) {
+        /* there was no existing ACE for this type/id */
+        EC_ZERO_LOG(acl_create_entry(aclp, &e));
+        EC_ZERO_LOG(acl_set_tag_type(e, type));
+        EC_ZERO_LOG(acl_set_qualifier(e, &id));
+        EC_ZERO_LOG(acl_get_permset(e, &permset));
+        EC_ZERO_LOG(acl_clear_perms(permset));
+        EC_ZERO_LOG(acl_add_perm(permset, perm));
+        EC_ZERO_LOG(acl_set_permset(e, permset));
+    }
+    
+EC_CLEANUP:
+    if (eid) acl_free(eid);
+
+    EC_EXIT;
+}
+
+/*!
+ * Map Darwin ACL to POSIX ACL.
+ *
+ * aclp must point to a acl_init'ed acl_t or an acl_t that can eg contain default ACEs.
+ * Mapping pecularities:
+ * - we create a default ace (which inherits to files and dirs) if either
+     DARWIN_ACE_FLAGS_FILE_INHERIT or DARWIN_ACE_FLAGS_DIRECTORY_INHERIT is requested
+ * - we throw away DARWIN_ACE_FLAGS_LIMIT_INHERIT (can't be mapped), thus the ACL will
+ *   not be limited
+ *
+ * @param darwin_aces  (r)  pointer to darwin_aces buffer
+ * @param def_aclp     (rw) directories: pointer to an initialized acl_t with the default acl
+ *                          files: *def_aclp will be NULL
+ * @param acc_aclp     (rw) pointer to an initialized acl_t with the access acl
+ * @param ace_count    (r)  number of ACEs in darwin_aces buffer
+ *
+ * @returns 0 on success storing the result in aclp, -1 on error.
+ */
+static int map_aces_darwin_to_posix(const darwin_ace_t *darwin_aces,
+                                    acl_t *def_aclp,
+                                    acl_t *acc_aclp,
+                                    int ace_count)
+{
+    EC_INIT;
+    char *name = NULL;
+    uuidtype_t uuidtype;
+    struct passwd *pwd;
+    struct group *grp;
+    uid_t id;
+    uint32_t darwin_ace_flags, darwin_ace_rights;
+    acl_tag_t tag;
+    acl_perm_t perm;
+
+    for ( ; ace_count != 0; ace_count--, darwin_aces++) {
+        /* type: allow/deny, posix only has allow */
+        darwin_ace_flags = ntohl(darwin_aces->darwin_ace_flags);
+        if ( ! (darwin_ace_flags & DARWIN_ACE_FLAGS_PERMIT))
+            continue;
+
+        darwin_ace_rights = ntohl(darwin_aces->darwin_ace_rights);
+        perm = map_darwin_right_to_posix_permset(darwin_ace_rights, (*def_aclp != NULL));
+        if (perm == 0)
+            continue;       /* dont add empty perm */
+
+        LOG(log_debug, logtype_afpd, "map_ace: no: %u, flags: %08x, darwin: %08x, posix: %02x",
+            ace_count, darwin_ace_flags, darwin_ace_rights, perm);
+
+         /* uid/gid */
+        EC_ZERO_LOG(getnamefromuuid(darwin_aces->darwin_ace_uuid, &name, &uuidtype));
+        switch (uuidtype) {
+        case UUID_LOCAL:
+            free(name);
+            name = NULL;
+            continue;
+        case UUID_USER:
+            EC_NULL_LOG(pwd = getpwnam(name));
+            tag = ACL_USER;
+            id = pwd->pw_uid;
+            LOG(log_debug, logtype_afpd, "map_ace: name: %s, uid: %u", name, id);
+            break;
+        case UUID_GROUP:
+            EC_NULL_LOG(grp = getgrnam(name));
+            tag = ACL_GROUP;
+            id = (uid_t)(grp->gr_gid);
+            LOG(log_debug, logtype_afpd, "map_ace: name: %s, gid: %u", name, id);
+            break;
+        }
+        free(name);
+        name = NULL;
+
+        if (darwin_ace_flags & DARWIN_ACE_INHERIT_CONTROL_FLAGS) {
+            if (*def_aclp == NULL) {
+                /* ace request inheritane but we haven't got a default acl pointer */
+                LOG(log_warning, logtype_afpd, "map_acl: unexpected ACE, flags: 0x%04x",
+                    darwin_ace_flags);
+                EC_FAIL;
+            }
+            /* add it as default ace */
+            EC_ZERO_LOG(posix_acl_add_perm(def_aclp, tag, id, perm));
+
+
+            if (! (darwin_ace_flags & DARWIN_ACE_FLAGS_ONLY_INHERIT))
+                /* if it not a "inherit only" ace, it must be added as access aces too */
+                EC_ZERO_LOG(posix_acl_add_perm(acc_aclp, tag, id, perm));
+        } else {
+            EC_ZERO_LOG(posix_acl_add_perm(acc_aclp, tag, id, perm));
+        }
+    }
+
+EC_CLEANUP:
+    if (name)
+        free(name);
+
+    EC_EXIT;
+}
+
+/*
+ * Map ACEs from POSIX to Darwin.
+ * type is either POSIX_DEFAULT_2_DARWIN or POSIX_ACCESS_2_DARWIN, cf. acl_get_file.
+ * Return number of mapped ACES, -1 on error.
+ */
+static int map_acl_posix_to_darwin(int type, const acl_t acl, darwin_ace_t *darwin_aces)
+{
+    EC_INIT;
+    int mapped_aces = 0;
+    int entry_id = ACL_FIRST_ENTRY;
+    acl_entry_t e;
+    acl_tag_t tag;
+    uid_t *uid = NULL;
+    gid_t *gid = NULL;
+    struct passwd *pwd = NULL;
+    struct group *grp = NULL;
+    uint32_t flags;
+    uint32_t rights, maskrights = 0;
+    darwin_ace_t *saved_darwin_aces = darwin_aces;
+
+    LOG(log_maxdebug, logtype_afpd, "map_aces_posix_to_darwin(%s)",
+        (type & MAP_MASK) == POSIX_DEFAULT_2_DARWIN ?
+        "POSIX_DEFAULT_2_DARWIN" : "POSIX_ACCESS_2_DARWIN");
+
+    /* itereate through all ACEs */
+    while (acl_get_entry(acl, entry_id, &e) == 1) {
+        entry_id = ACL_NEXT_ENTRY;
+
+        /* get ACE type */
+        EC_ZERO_LOG(acl_get_tag_type(e, &tag));
+
+        /* we return user and group ACE */
+        switch (tag) {
+        case ACL_USER:
+            EC_NULL_LOG(uid = (uid_t *)acl_get_qualifier(e));
+            EC_NULL_LOG(pwd = getpwuid(*uid));
+            LOG(log_debug, logtype_afpd, "map_aces_posix_to_darwin: uid: %d -> name: %s",
+                *uid, pwd->pw_name);
+            EC_ZERO_LOG(getuuidfromname(pwd->pw_name, UUID_USER, darwin_aces->darwin_ace_uuid));
+            acl_free(uid);
+            uid = NULL;
+            break;
+
+        case ACL_GROUP:
+            EC_NULL_LOG(gid = (gid_t *)acl_get_qualifier(e));
+            EC_NULL_LOG(grp = getgrgid(*gid));
+            LOG(log_debug, logtype_afpd, "map_aces_posix_to_darwin: gid: %d -> name: %s",
+                *gid, grp->gr_name);
+            EC_ZERO_LOG(getuuidfromname(grp->gr_name, UUID_GROUP, darwin_aces->darwin_ace_uuid));
+            acl_free(gid);
+            gid = NULL;
+            break;
+
+        case ACL_MASK:
+            maskrights = posix_permset_to_darwin_rights(e, type & IS_DIR);
+            continue;
+
+        default:
+            continue;
+        }
+
+        /* flags */
+        flags = DARWIN_ACE_FLAGS_PERMIT;
+        if ((type & MAP_MASK) == POSIX_DEFAULT_2_DARWIN)
+            flags |= DARWIN_ACE_FLAGS_FILE_INHERIT
+                | DARWIN_ACE_FLAGS_DIRECTORY_INHERIT
+                | DARWIN_ACE_FLAGS_ONLY_INHERIT;
+        darwin_aces->darwin_ace_flags = htonl(flags);
+
+        /* rights */
+        rights = posix_permset_to_darwin_rights(e, type & IS_DIR);
+        darwin_aces->darwin_ace_rights = htonl(rights);
+
+        darwin_aces++;
+        mapped_aces++;
+    } /* while */
+
+    /* Loop through the mapped ACE buffer once again, applying the mask */
+    for (int i = mapped_aces; i > 0; i--) {
+        saved_darwin_aces->darwin_ace_rights &= htonl(maskrights);
+        saved_darwin_aces++;
+    }
+
+    EC_STATUS(mapped_aces);
+
+EC_CLEANUP:
+    if (uid) acl_free(uid);
+    if (gid) acl_free(gid);
+    EC_EXIT;
+}
+#endif
+
+/*
+ * Multiplex ACL mapping (SOLARIS_2_DARWIN, DARWIN_2_SOLARIS, POSIX_2_DARWIN, DARWIN_2_POSIX).
+ * Reads from 'aces' buffer, writes to 'rbuf' buffer.
+ * Caller must provide buffer.
+ * Darwin ACEs are read and written in network byte order.
+ * Needs to know how many ACEs are in the ACL (ace_count) for Solaris ACLs.
+ * Ignores trivial ACEs.
+ * Return no of mapped ACEs or -1 on error.
+ */
+static int map_acl(int type, void *acl, darwin_ace_t *buf, int ace_count)
 {
     int mapped_aces;
 
     LOG(log_debug9, logtype_afpd, "map_acl: BEGIN");
 
-    switch (type) {
+    switch (type & MAP_MASK) {
+
+#ifdef HAVE_SOLARIS_ACLS
     case SOLARIS_2_DARWIN:
-        mapped_aces = map_aces_solaris_to_darwin( nfsv4_aces, buf, ace_count);
+        mapped_aces = map_aces_solaris_to_darwin( acl, buf, ace_count);
         break;
 
     case DARWIN_2_SOLARIS:
-        mapped_aces = map_aces_darwin_to_solaris( buf, nfsv4_aces, ace_count);
+        mapped_aces = map_aces_darwin_to_solaris( buf, acl, ace_count);
+        break;
+#endif /* HAVE_SOLARIS_ACLS */
+
+#ifdef HAVE_POSIX_ACLS
+    case POSIX_DEFAULT_2_DARWIN:
+        mapped_aces = map_acl_posix_to_darwin(type, (const acl_t)acl, buf);
+        break;
+
+    case POSIX_ACCESS_2_DARWIN:
+        mapped_aces = map_acl_posix_to_darwin(type, (const acl_t)acl, buf);
+        break;
+
+    case DARWIN_2_POSIX_DEFAULT:
+        break;
+
+    case DARWIN_2_POSIX_ACCESS:
         break;
+#endif /* HAVE_POSIX_ACLS */
 
     default:
         mapped_aces = -1;
@@ -386,20 +814,22 @@ static int map_acl(int type, ace_t *nfsv4_aces, darwin_ace_t *buf, int ace_count
     return mapped_aces;
 }
 
-/********************************************************
- * 1st level funcs
- ********************************************************/
-
-
 /* Get ACL from object omitting trivial ACEs. Map to Darwin ACL style and
    store Darwin ACL at rbuf. Add length of ACL written to rbuf to *rbuflen.
    Returns 0 on success, -1 on error. */
 static int get_and_map_acl(char *name, char *rbuf, size_t *rbuflen)
 {
-    int ace_count, mapped_aces, err;
-    ace_t *aces;
+    EC_INIT;
+    int mapped_aces = 0;
+    int dirflag;
     uint32_t *darwin_ace_count = (u_int32_t *)rbuf;
-
+#ifdef HAVE_SOLARIS_ACLS
+    int ace_count = 0;
+    ace_t *aces = NULL;
+#endif
+#ifdef HAVE_POSIX_ACLS
+    struct stat st;
+#endif
     LOG(log_debug9, logtype_afpd, "get_and_map_acl: BEGIN");
 
     /* Skip length and flags */
@@ -407,38 +837,72 @@ static int get_and_map_acl(char *name, char *rbuf, size_t *rbuflen)
     *rbuf = 0;
     rbuf += 4;
 
-    if ( (ace_count = get_nfsv4_acl(name, &aces)) == -1) {
-        LOG(log_error, logtype_afpd, "get_and_map_acl: couldnt get ACL");
-        return -1;
+#ifdef HAVE_SOLARIS_ACLS
+    EC_NEG1(ace_count = get_nfsv4_acl(name, &aces));
+    EC_NEG1(mapped_aces = map_acl(SOLARIS_2_DARWIN, aces, (darwin_ace_t *)rbuf, ace_count));
+#endif /* HAVE_SOLARIS_ACLS */
+
+#ifdef HAVE_POSIX_ACLS
+    acl_t defacl = NULL , accacl = NULL;
+
+    /* stat to check if its a dir */
+    EC_ZERO_LOG(lstat(name, &st));
+
+    /* if its a dir, check for default acl too */
+    dirflag = 0;
+    if (S_ISDIR(st.st_mode)) {
+        dirflag = IS_DIR;
+        EC_NULL_LOG(defacl = acl_get_file(name, ACL_TYPE_DEFAULT));
+        EC_NEG1(mapped_aces = map_acl(POSIX_DEFAULT_2_DARWIN | dirflag,
+                                      defacl,
+                                      (darwin_ace_t *)rbuf,
+                                      0));
     }
 
-    if ( (mapped_aces = map_acl(SOLARIS_2_DARWIN, aces, (darwin_ace_t *)rbuf, ace_count)) == -1) {
-        err = -1;
-        goto cleanup;
-    }
+    EC_NULL_LOG(accacl = acl_get_file(name, ACL_TYPE_ACCESS));
+
+    int tmp;
+    EC_NEG1(tmp = map_acl(POSIX_ACCESS_2_DARWIN | dirflag,
+                          accacl,
+                          (darwin_ace_t *)(rbuf + mapped_aces * sizeof(darwin_ace_t)),
+                          0));
+    mapped_aces += tmp;
+#endif /* HAVE_POSIX_ACLS */
+
     LOG(log_debug, logtype_afpd, "get_and_map_acl: mapped %d ACEs", mapped_aces);
 
-    err = 0;
     *darwin_ace_count = htonl(mapped_aces);
     *rbuflen += sizeof(darwin_acl_header_t) + (mapped_aces * sizeof(darwin_ace_t));
 
-cleanup:
-    free(aces);
+    EC_STATUS(0);
+
+EC_CLEANUP:
+#ifdef HAVE_SOLARIS_ACLS
+    if (aces) free(aces);
+#endif
+#ifdef HAVE_POSIX_ACLS
+    if (defacl) acl_free(defacl);
+    if (accacl) acl_free(accacl);
+#endif /* HAVE_POSIX_ACLS */
 
     LOG(log_debug9, logtype_afpd, "get_and_map_acl: END");
-    return err;
+
+    EC_EXIT;
 }
 
 /* Removes all non-trivial ACLs from object. Returns full AFPERR code. */
-static int remove_acl_vfs(const struct vol *vol,const char *path, int dir)
+static int remove_acl(const struct vol *vol,const char *path, int dir)
 {
-    int ret;
+    int ret = AFP_OK;
 
+#if (defined HAVE_SOLARIS_ACLS || defined HAVE_POSIX_ACLS)
     /* Ressource etc. first */
     if ((ret = vol->vfs->vfs_remove_acl(vol, path, dir)) != AFP_OK)
         return ret;
     /* now the data fork or dir */
-    return (remove_acl(path));
+    ret = remove_acl_vfs(path);
+#endif
+    return ret;
 }
 
 /*
@@ -449,19 +913,21 @@ static int remove_acl_vfs(const struct vol *vol,const char *path, int dir)
   We will store inherited ACEs first, which is Darwins canonical order.
   - returns AFPerror code
 */
-static int set_acl_vfs(const struct vol *vol, char *name, int inherit, char *ibuf)
+#ifdef HAVE_SOLARIS_ACLS
+static int set_acl(const struct vol *vol,
+                   char *name,
+                   int inherit,
+                   darwin_ace_t *daces,
+                   uint32_t ace_count)
 {
-    int ret, i, nfsv4_ace_count, tocopy_aces_count = 0, new_aces_count = 0, trivial_ace_count = 0;
+    EC_INIT;
+    int i, nfsv4_ace_count;
+    int tocopy_aces_count = 0, new_aces_count = 0, trivial_ace_count = 0;
     ace_t *old_aces, *new_aces = NULL;
     uint16_t flags;
-    uint32_t ace_count;
 
     LOG(log_debug9, logtype_afpd, "set_acl: BEGIN");
 
-    /*  Get no of ACEs the client put on the wire */
-    ace_count = htonl(*((uint32_t *)ibuf));
-    ibuf += 8;      /* skip ACL flags (see acls.h) */
-
     if (inherit)
         /* inherited + trivial ACEs */
         flags = ACE_INHERITED_ACE | ACE_OWNER | ACE_GROUP | ACE_EVERYONE;
@@ -478,11 +944,10 @@ static int set_acl_vfs(const struct vol *vol, char *name, int inherit, char *ibu
     }
 
     /* Now malloc buffer exactly sized to fit all new ACEs */
-    new_aces = malloc( (ace_count + tocopy_aces_count) * sizeof(ace_t) );
-    if (new_aces == NULL) {
+    if ((new_aces = malloc((ace_count + tocopy_aces_count) * sizeof(ace_t))) == NULL) {
         LOG(log_error, logtype_afpd, "set_acl: malloc %s", strerror(errno));
-        ret = AFPERR_MISC;
-        goto cleanup;
+        EC_STATUS(AFPERR_MISC);
+        goto EC_CLEANUP;
     }
 
     /* Start building new ACL */
@@ -500,13 +965,15 @@ static int set_acl_vfs(const struct vol *vol, char *name, int inherit, char *ibu
     LOG(log_debug7, logtype_afpd, "set_acl: copied %d inherited ACEs", new_aces_count);
 
     /* Now the ACEs from the client */
-    ret = map_acl(DARWIN_2_SOLARIS, &new_aces[new_aces_count], (darwin_ace_t *)ibuf, ace_count);
-    if (ret == -1) {
-        ret = AFPERR_PARAM;
-        goto cleanup;
+    if ((ret = (map_acl(DARWIN_2_SOLARIS,
+                        &new_aces[new_aces_count],
+                        daces,
+                        ace_count))) == -1) {
+        EC_STATUS(AFPERR_PARAM);
+        goto EC_CLEANUP;
     }
-    new_aces_count += ace_count;
-    LOG(log_debug7, logtype_afpd, "set_acl: mapped %d ACEs from client", ace_count);
+    new_aces_count += ret;
+    LOG(log_debug7, logtype_afpd, "set_acl: mapped %d ACEs from client", ret);
 
     /* Now copy the trivial ACEs */
     for (i=0; i < nfsv4_ace_count; i++) {
@@ -521,197 +988,197 @@ static int set_acl_vfs(const struct vol *vol, char *name, int inherit, char *ibu
     /* Ressourcefork first.
        Note: for dirs we set the same ACL on the .AppleDouble/.Parent _file_. This
        might be strange for ACE_DELETE_CHILD and for inheritance flags. */
-    if ( (ret = vol->vfs->vfs_acl(vol, name, ACE_SETACL, new_aces_count, new_aces)) != 0) {
+    if ((ret = (vol->vfs->vfs_acl(vol, name, ACE_SETACL, new_aces_count, new_aces))) != 0) {
         LOG(log_error, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
         if (errno == (EACCES | EPERM))
-            ret = AFPERR_ACCESS;
+            EC_STATUS(AFPERR_ACCESS);
         else if (errno == ENOENT)
-            ret = AFPERR_NOITEM;
+            EC_STATUS(AFPERR_NOITEM);
         else
-            ret = AFPERR_MISC;
-        goto cleanup;
+            EC_STATUS(AFPERR_MISC);
+        goto EC_CLEANUP;
     }
-    if ( (ret = acl(name, ACE_SETACL, new_aces_count, new_aces)) != 0) {
+    if ((ret = (acl(name, ACE_SETACL, new_aces_count, new_aces))) != 0) {
         LOG(log_error, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
         if (errno == (EACCES | EPERM))
-            ret = AFPERR_ACCESS;
+            EC_STATUS(AFPERR_ACCESS);
         else if (errno == ENOENT)
-            ret = AFPERR_NOITEM;
+            EC_STATUS(AFPERR_NOITEM);
         else
-            ret = AFPERR_MISC;
-        goto cleanup;
+            EC_STATUS(AFPERR_MISC);
+        goto EC_CLEANUP;
     }
 
-    ret = AFP_OK;
+    EC_STATUS(AFP_OK);
 
-cleanup:
-    free(old_aces);
-    free(new_aces);
+EC_CLEANUP:
+    if (old_aces) free(old_aces);
+    if (new_aces) free(new_aces);
 
     LOG(log_debug9, logtype_afpd, "set_acl: END");
-    return ret;
+    EC_EXIT;
 }
-
-/*
-  Checks if a given UUID has requested_rights(type darwin_ace_rights) for path.
-  Note: this gets called frequently and is a good place for optimizations !
-*/
-static int check_acl_access(const char *path, const uuidp_t uuid, uint32_t requested_darwin_rights)
+#endif /* HAVE_SOLARIS_ACLS */
+
+#ifdef HAVE_POSIX_ACLS
+static int set_acl(const struct vol *vol,
+                   const char *name,
+                   int inherit _U_,
+                   darwin_ace_t *daces,
+                   uint32_t ace_count)
 {
-    int                 ret, i, ace_count, dir, checkgroup;
-    char                *username; /* might be group too */
-    uuidtype_t          uuidtype;
-    uid_t               uid;
-    gid_t               pgid;
-    uint32_t            requested_rights = 0, allowed_rights = 0, denied_rights = 0;
-    ace_t               *aces;
-    struct passwd       *pwd;
-    struct stat         st;
-    int                 check_user_trivace = 0, check_group_trivace = 0;
-    uid_t               who;
-    uint16_t            flags;
-    uint16_t            type;
-    uint32_t            rights;
-
-#ifdef DEBUG
-    LOG(log_debug9, logtype_afpd, "check_access: BEGIN. Request: %08x", requested_darwin_rights);
-#endif
-    /* Get uid or gid from UUID */
-    if ( (getnamefromuuid(uuid, &username, &uuidtype)) != 0) {
-        LOG(log_error, logtype_afpd, "check_access: error getting name from UUID");
-        return AFPERR_PARAM;
-    }
+    EC_INIT;
+    acl_t def_acl = NULL;
+    acl_t acc_acl = NULL;
 
-    /* File or dir */
-    if ((lstat(path, &st)) != 0) {
-        LOG(log_error, logtype_afpd, "check_access: stat: %s", strerror(errno));
-        ret = AFPERR_PARAM;
-        goto exit;
-    }
-    dir = S_ISDIR(st.st_mode);
+    LOG(log_maxdebug, logtype_afpd, "set_acl: BEGIN");
 
-    if (uuidtype == UUID_USER) {
-        pwd = getpwnam(username);
-        if (!pwd) {
-            LOG(log_error, logtype_afpd, "check_access: getpwnam: %s", strerror(errno));
-            ret = AFPERR_MISC;
-            goto exit;
-        }
-        uid = pwd->pw_uid;
-        pgid = pwd->pw_gid;
+    struct stat st;
+    EC_ZERO_LOG_ERR(lstat(name, &st), AFPERR_NOOBJ);
 
-        /* If user is file/dir owner we must check the user trivial ACE */
-        if (uid == st.st_uid) {
-            LOG(log_debug, logtype_afpd, "check_access: user: %s is files owner. Must check trivial user ACE", username);
-            check_user_trivace = 1;
-        }
+    /* seed default ACL with access ACL */
+    if (S_ISDIR(st.st_mode))
+        EC_NULL_LOG_ERR(def_acl = acl_get_file(name, ACL_TYPE_ACCESS), AFPERR_MISC);
 
-        /* Now check if requested user is files owning group. If yes we must check the group trivial ACE */
-        if ( (check_group(username, uid, pgid, st.st_gid)) == 0) {
-            LOG(log_debug, logtype_afpd, "check_access: user: %s is in group: %d. Must check trivial group ACE", username, st.st_gid);
-            check_group_trivace = 1;
-        }
-    } else { /* hopefully UUID_GROUP*/
-        LOG(log_error, logtype_afpd, "check_access: afp_access for UUID of groups not supported!");
-#if 0
-        grp = getgrnam(username);
-        if (!grp) {
-            LOG(log_error, logtype_afpd, "check_access: getgrnam: %s", strerror(errno));
-            return -1;
-        }
-        if (st.st_gid == grp->gr_gid )
-            check_group_trivace = 1;
-#endif
-    }
+    /* for files def_acl will be NULL */
 
-    /* Map requested rights to Solaris style. */
-    for (i=0; darwin_to_nfsv4_rights[i].from != 0; i++) {
-        if (requested_darwin_rights & darwin_to_nfsv4_rights[i].from)
-            requested_rights |= darwin_to_nfsv4_rights[i].to;
-    }
+    /* create access acl from mode */
+    EC_NULL_LOG_ERR(acc_acl = acl_from_mode(st.st_mode), AFPERR_MISC);
 
-    /* Get ACL from file/dir */
-    if ( (ace_count = get_nfsv4_acl(path, &aces)) == -1) {
-        LOG(log_error, logtype_afpd, "check_access: error getting ACEs");
-        ret = AFPERR_MISC;
-        goto exit;
+    /* adds the clients aces */
+    EC_ZERO_ERR(map_aces_darwin_to_posix(daces, &def_acl, &acc_acl, ace_count), AFPERR_MISC);
+
+    /* calcuate ACL mask */
+    EC_ZERO_LOG_ERR(acl_calc_mask(&acc_acl), AFPERR_MISC);
+
+    /* is it ok? */
+    EC_ZERO_LOG_ERR(acl_valid(acc_acl), AFPERR_MISC);
+
+    /* set it */
+    EC_ZERO_LOG_ERR(acl_set_file(name, ACL_TYPE_ACCESS, acc_acl), AFPERR_MISC);
+    EC_ZERO_LOG_ERR(vol->vfs->vfs_acl(vol, name, ACL_TYPE_ACCESS, 0, acc_acl), AFPERR_MISC);
+
+    if (def_acl) {
+        EC_ZERO_LOG_ERR(acl_set_file(name, ACL_TYPE_DEFAULT, def_acl), AFPERR_MISC);
+        EC_ZERO_LOG_ERR(vol->vfs->vfs_acl(vol, name, ACL_TYPE_DEFAULT, 0, def_acl), AFPERR_MISC);
     }
-    /* Now check requested rights */
-    ret = AFPERR_ACCESS;
-    i = 0;
-    do { /* Loop through ACEs */
-        who = aces[i].a_who;
-        flags = aces[i].a_flags;
-        type = aces[i].a_type;
-        rights = aces[i].a_access_mask;
 
-        if (flags & ACE_INHERIT_ONLY_ACE)
-            continue;
+EC_CLEANUP:
+    acl_free(acc_acl);
+    acl_free(def_acl);
 
-        /* Check if its a group ACE and set checkgroup to 1 if yes */
-        checkgroup = 0;
-        if ( (flags & ACE_IDENTIFIER_GROUP) && !(flags & ACE_GROUP) ) {
-            if ( (check_group(username, uid, pgid, who)) == 0)
-                checkgroup = 1;
-            else
-                continue;
-        }
+    LOG(log_maxdebug, logtype_afpd, "set_acl: END");
+    EC_EXIT;
+}
+#endif /* HAVE_POSIX_ACLS */
+
+/*!
+ * Checks if a given UUID has requested_rights(type darwin_ace_rights) for path.
+ *
+ * Note: this gets called frequently and is a good place for optimizations !
+ *
+ * @param vol              (r) volume
+ * @param dir              (r) directory
+ * @param path             (r) path to filesystem object
+ * @param uuid             (r) UUID of user
+ * @param requested_rights (r) requested Darwin ACE
+ *
+ * @returns                    AFP result code
+*/
+static int check_acl_access(const struct vol *vol,
+                            const struct dir *dir,
+                            const char *path,
+                            const uuidp_t uuid,
+                            uint32_t requested_rights)
+{
+    int            ret;
+    uint32_t       allowed_rights = 0;
+    char           *username = NULL;
+    uuidtype_t     uuidtype;
+    struct stat    st;
+    bstring        parent = NULL;
 
-        /* Now the tricky part: decide if ACE effects our user. I'll explain:
-           if its a dedicated (non trivial) ACE for the user
-           OR
-           if its a ACE for a group we're member of
-           OR
-           if its a trivial ACE_OWNER ACE and requested UUID is the owner
-           OR
-           if its a trivial ACE_GROUP ACE and requested UUID is group
-           OR
-           if its a trivial ACE_EVERYONE ACE
-           THEN
-           process ACE */
-        if (
-            ( (who == uid) && !(flags & (ACE_TRIVIAL|ACE_IDENTIFIER_GROUP)) ) ||
-            (checkgroup) ||
-            ( (flags & ACE_OWNER) && check_user_trivace ) ||
-            ( (flags & ACE_GROUP) && check_group_trivace ) ||
-            ( flags & ACE_EVERYONE )
-            ) {
-            /* Found an applicable ACE */
-            if (type == ACE_ACCESS_ALLOWED_ACE_TYPE)
-                allowed_rights |= rights;
-            else if (type == ACE_ACCESS_DENIED_ACE_TYPE)
-                /* Only or to denied rights if not previously allowed !! */
-                denied_rights |= ((!allowed_rights) & rights);
+    LOG(log_maxdebug, logtype_afpd, "check_access: Request: 0x%08x", requested_rights);
+
+    /* Get uid or gid from UUID */
+    EC_ZERO_LOG_ERR(getnamefromuuid(uuid, &username, &uuidtype), AFPERR_PARAM);
+    EC_ZERO_LOG_ERR(lstat(path, &st), AFPERR_PARAM);
+
+    switch (uuidtype) {
+    case UUID_USER:
+        break;
+    case UUID_GROUP:
+        LOG(log_warning, logtype_afpd, "check_access: afp_access not supported for groups");
+        EC_STATUS(AFPERR_MISC);
+        goto EC_CLEANUP;
+
+    case UUID_LOCAL:
+        LOG(log_warning, logtype_afpd, "check_access: local UUID");
+        EC_STATUS(AFPERR_MISC);
+        goto EC_CLEANUP;
+    }
+
+#ifdef HAVE_SOLARIS_ACLS
+    EC_ZERO_LOG(solaris_acl_rights(path, &st, &allowed_rights));
+#endif
+#ifdef HAVE_POSIX_ACLS
+    EC_ZERO_LOG(posix_acl_rights(path, &st, &allowed_rights));
+#endif
+
+    LOG(log_debug, logtype_afpd, "allowed rights: 0x%08x", allowed_rights);
+
+    /*
+     * The DARWIN_ACE_DELETE right might implicitly result from write acces to the parent
+     * directory. As it seems the 10.6 AFP client is puzzled when this right is not
+     * allowed where a delete would succeed because the parent dir gives write perms.
+     * So we check the parent dir for write access and set the right accordingly.
+     * Currentyl acl2ownermode calls us with dir = NULL, because it doesn't make sense
+     * there to do this extra check -- afaict.
+     */
+    if (vol && dir && (requested_rights & DARWIN_ACE_DELETE)) {
+        int i;
+        uint32_t parent_rights = 0;
+
+        if (dir->d_did == DIRDID_ROOT_PARENT) {
+            /* use volume path */
+            EC_NULL_LOG_ERR(parent = bfromcstr(vol->v_path), AFPERR_MISC);
+        } else {
+            /* build path for parent */
+            EC_NULL_LOG_ERR(parent = bstrcpy(dir->d_fullpath), AFPERR_MISC);
+            EC_ZERO_LOG_ERR(bconchar(parent, '/'), AFPERR_MISC);
+            EC_ZERO_LOG_ERR(bcatcstr(parent, path), AFPERR_MISC);
+            EC_NEG1_LOG_ERR(i = bstrrchr(parent, '/'), AFPERR_MISC);
+            EC_ZERO_LOG_ERR(binsertch(parent, i, 1, 0), AFPERR_MISC);
         }
-    } while (++i < ace_count);
 
+        LOG(log_debug, logtype_afpd,"parent: %s", cfrombstr(parent));
+        EC_ZERO_LOG_ERR(lstat(cfrombstr(parent), &st), AFPERR_MISC);
 
-    /* Darwin likes to ask for "delete_child" on dir,
-       "write_data" is actually the same, so we add that for dirs */
-    if (dir && (allowed_rights & ACE_WRITE_DATA))
-        allowed_rights |= ACE_DELETE_CHILD;
+#ifdef HAVE_SOLARIS_ACLS
+        EC_ZERO_LOG(solaris_acl_rights(cfrombstr(parent), &st, &parent_rights));
+#endif
+#ifdef HAVE_POSIX_ACLS
+    EC_ZERO_LOG(posix_acl_rights(path, &st, &allowed_rights));
+#endif
+        if (parent_rights & (DARWIN_ACE_WRITE_DATA | DARWIN_ACE_DELETE_CHILD))
+            allowed_rights |= DARWIN_ACE_DELETE; /* man, that was a lot of work! */
+    }
 
-    if (requested_rights & denied_rights) {
-        LOG(log_debug, logtype_afpd, "check_access: some requested right was denied:");
-        ret = AFPERR_ACCESS;
-    } else if ((requested_rights & allowed_rights) != requested_rights) {
-        LOG(log_debug, logtype_afpd, "check_access: some requested right wasn't allowed:");
-        ret = AFPERR_ACCESS;
+    if ((requested_rights & allowed_rights) != requested_rights) {
+        LOG(log_debug, logtype_afpd, "some requested right wasn't allowed: 0x%08x / 0x%08x",
+            requested_rights, allowed_rights);
+        EC_STATUS(AFPERR_ACCESS);
     } else {
-        LOG(log_debug, logtype_afpd, "check_access: all requested rights are allowed:");
-        ret = AFP_OK;
+        LOG(log_debug, logtype_afpd, "all requested rights are allowed: 0x%08x",
+            requested_rights);
+        EC_STATUS(AFP_OK);
     }
 
-    LOG(log_debug, logtype_afpd, "check_access: Requested rights: %08x, allowed_rights: %08x, denied_rights: %08x, Result: %d",
-        requested_rights, allowed_rights, denied_rights, ret);
+EC_CLEANUP:
+    if (username) free(username);
+    if (parent) bdestroy(parent);
 
-exit:
-    free(aces);
-    free(username);
-#ifdef DEBUG
-    LOG(log_debug9, logtype_afpd, "check_access: END");
-#endif
-    return ret;
+    EC_EXIT;
 }
 
 /********************************************************
@@ -728,8 +1195,6 @@ int afp_access(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
     struct path         *s_path;
     uuidp_t             uuid;
 
-    LOG(log_debug9, logtype_afpd, "afp_access: BEGIN");
-
     *rbuflen = 0;
     ibuf += 2;
 
@@ -771,9 +1236,8 @@ int afp_access(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
         return AFPERR_NOOBJ;
     }
 
-    ret = check_acl_access(s_path->u_name, uuid, darwin_ace_rights);
+    ret = check_acl_access(vol, dir, s_path->u_name, uuid, darwin_ace_rights);
 
-    LOG(log_debug9, logtype_afpd, "afp_access: END");
     return ret;
 }
 
@@ -831,11 +1295,14 @@ int afp_getacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
     /* Shall we return owner UUID ? */
     if (bitmap & kFileSec_UUID) {
         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files owner user UUID");
-        if (NULL == (pw = getpwuid(s_path->st.st_uid)))
-            return AFPERR_MISC;
-        LOG(log_debug, logtype_afpd, "afp_getacl: got uid: %d, name: %s", s_path->st.st_uid, pw->pw_name);
-        if ((ret = getuuidfromname(pw->pw_name, UUID_USER, rbuf)) != 0)
-            return AFPERR_MISC;
+        if (NULL == (pw = getpwuid(s_path->st.st_uid))) {
+            LOG(log_debug, logtype_afpd, "afp_getacl: local uid: %u", s_path->st.st_uid);
+            localuuid_from_id(rbuf, UUID_USER, s_path->st.st_uid);
+        } else {
+            LOG(log_debug, logtype_afpd, "afp_getacl: got uid: %d, name: %s", s_path->st.st_uid, pw->pw_name);
+            if ((ret = getuuidfromname(pw->pw_name, UUID_USER, rbuf)) != 0)
+                return AFPERR_MISC;
+        }
         rbuf += UUID_BINSIZE;
         *rbuflen += UUID_BINSIZE;
     }
@@ -843,11 +1310,14 @@ int afp_getacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
     /* Shall we return group UUID ? */
     if (bitmap & kFileSec_GRPUUID) {
         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files owner group UUID");
-        if (NULL == (gr = getgrgid(s_path->st.st_gid)))
-            return AFPERR_MISC;
-        LOG(log_debug, logtype_afpd, "afp_getacl: got gid: %d, name: %s", s_path->st.st_gid, gr->gr_name);
-        if ((ret = getuuidfromname(gr->gr_name, UUID_GROUP, rbuf)) != 0)
-            return AFPERR_MISC;
+        if (NULL == (gr = getgrgid(s_path->st.st_gid))) {
+            LOG(log_debug, logtype_afpd, "afp_getacl: local gid: %u", s_path->st.st_gid);
+            localuuid_from_id(rbuf, UUID_GROUP, s_path->st.st_gid);
+        } else {
+            LOG(log_debug, logtype_afpd, "afp_getacl: got gid: %d, name: %s", s_path->st.st_gid, gr->gr_name);
+            if ((ret = getuuidfromname(gr->gr_name, UUID_GROUP, rbuf)) != 0)
+                return AFPERR_MISC;
+        }
         rbuf += UUID_BINSIZE;
         *rbuflen += UUID_BINSIZE;
     }
@@ -855,7 +1325,11 @@ int afp_getacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
     /* Shall we return ACL ? */
     if (bitmap & kFileSec_ACL) {
         LOG(log_debug, logtype_afpd, "afp_getacl: client requested files ACL");
-        get_and_map_acl(s_path->u_name, rbuf, rbuflen);
+        if (get_and_map_acl(s_path->u_name, rbuf, rbuflen) != 0) {
+            LOG(log_error, logtype_afpd, "afp_getacl(\"%s/%s\"): mapping error",
+                getcwdpath(), s_path->u_name);
+            return AFPERR_MISC;
+        }
     }
 
     LOG(log_debug9, logtype_afpd, "afp_getacl: END");
@@ -914,14 +1388,14 @@ int afp_setacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
 
     /* Change owner: dont even try */
     if (bitmap & kFileSec_UUID) {
-        LOG(log_debug, logtype_afpd, "afp_setacl: change owner request. discarded.");
+        LOG(log_note, logtype_afpd, "afp_setacl: change owner request, discarded");
         ret = AFPERR_ACCESS;
         ibuf += UUID_BINSIZE;
     }
 
     /* Change group: certain changes might be allowed, so try it. FIXME: not implemented yet. */
     if (bitmap & kFileSec_UUID) {
-        LOG(log_debug, logtype_afpd, "afp_setacl: change group request. not supported this time.");
+        LOG(log_note, logtype_afpd, "afp_setacl: change group request, not supported");
         ret = AFPERR_PARAM;
         ibuf += UUID_BINSIZE;
     }
@@ -929,138 +1403,112 @@ int afp_setacl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
     /* Remove ACL ? */
     if (bitmap & kFileSec_REMOVEACL) {
         LOG(log_debug, logtype_afpd, "afp_setacl: Remove ACL request.");
-        if ((ret = remove_acl_vfs(vol, s_path->u_name, S_ISDIR(s_path->st.st_mode))) != AFP_OK)
+        if ((ret = remove_acl(vol, s_path->u_name, S_ISDIR(s_path->st.st_mode))) != AFP_OK)
             LOG(log_error, logtype_afpd, "afp_setacl: error from remove_acl");
     }
 
     /* Change ACL ? */
     if (bitmap & kFileSec_ACL) {
         LOG(log_debug, logtype_afpd, "afp_setacl: Change ACL request.");
-
-        /* Check if its our job to preserve inherited ACEs */
-        if (bitmap & kFileSec_Inherit)
-            ret = set_acl_vfs(vol, s_path->u_name, 1, ibuf);
-        else
-            ret = set_acl_vfs(vol, s_path->u_name, 0, ibuf);
+        /*  Get no of ACEs the client put on the wire */
+        uint32_t ace_count;
+        memcpy(&ace_count, ibuf, sizeof(uint32_t));
+        ace_count = htonl(ace_count);
+        ibuf += 8;      /* skip ACL flags (see acls.h) */
+
+        ret = set_acl(vol,
+                      s_path->u_name,
+                      (bitmap & kFileSec_Inherit),
+                      (darwin_ace_t *)ibuf,
+                      ace_count);
         if (ret == 0)
             ret = AFP_OK;
-        else
+        else {
+            LOG(log_warning, logtype_afpd, "afp_setacl(\"%s/%s\"): error",
+                getcwdpath(), s_path->u_name);
             ret = AFPERR_MISC;
+        }
     }
 
     LOG(log_debug9, logtype_afpd, "afp_setacl: END");
     return ret;
 }
 
-/*
-  unix.c/accessmode calls this: map ACL to OS 9 mode
-*/
-void acltoownermode(char *path, struct stat *st, uid_t uid, struct maccess *ma)
+/********************************************************************
+ * ACL funcs interfacing with other parts
+ ********************************************************************/
+
+/*!
+ * map ACL to user maccess
+ *
+ * This is the magic function that makes ACLs usable by calculating
+ * the access granted by ACEs to the logged in user.
+ */
+int acltoownermode(char *path, struct stat *st, struct maccess *ma)
 {
-    struct passwd *pw;
-    uuid_t uuid;
-    int dir, r_ok, w_ok, x_ok;
-
-    if ( ! (AFPobj->options.flags & OPTION_UUID))
-        return;
+    EC_INIT;
+    uint32_t rights = 0;
 
-    LOG(log_maxdebug, logtype_afpd, "acltoownermode('%s')", path);
+    if ( ! (AFPobj->options.flags & OPTION_ACL2MACCESS)
+         || (current_vol == NULL)
+         || ! (current_vol->v_flags & AFPVOL_ACLS))
+         return 0;
 
-    if ((pw = getpwuid(uid)) == NULL) {
-        LOG(log_error, logtype_afpd, "acltoownermode: %s", strerror(errno));
-        return;
-    }
+    LOG(log_maxdebug, logtype_afpd, "acltoownermode(\"%s/%s\", 0x%02x)",
+        getcwdpath(), path, ma->ma_user);
 
-    /* We need the UUID for check_acl_access */
-    if ((getuuidfromname(pw->pw_name, UUID_USER, uuid)) != 0)
-        return;
+#ifdef HAVE_SOLARIS_ACLS
+    EC_ZERO_LOG(solaris_acl_rights(path, st, &rights));
+#endif
+#ifdef HAVE_POSIX_ACLS
+    EC_ZERO_LOG(posix_acl_rights(path, st, &rights));
+#endif
 
-    /* These work for files and dirs */
-    r_ok = check_acl_access(path, uuid, DARWIN_ACE_READ_DATA);
-    w_ok = check_acl_access(path, uuid, (DARWIN_ACE_WRITE_DATA|DARWIN_ACE_APPEND_DATA));
-    x_ok = check_acl_access(path, uuid, DARWIN_ACE_EXECUTE);
+    LOG(log_maxdebug, logtype_afpd, "rights: 0x%08x", rights);
 
-    LOG(log_debug7, logtype_afpd, "acltoownermode: ma_user before: %04o",ma->ma_user);
-    if (r_ok == 0)
+    if (rights & DARWIN_ACE_READ_DATA)
         ma->ma_user |= AR_UREAD;
-    if (w_ok == 0)
+    if (rights & DARWIN_ACE_WRITE_DATA)
         ma->ma_user |= AR_UWRITE;
-    if (x_ok == 0)
+    if (rights & (DARWIN_ACE_EXECUTE | DARWIN_ACE_SEARCH))
         ma->ma_user |= AR_USEARCH;
-    LOG(log_debug7, logtype_afpd, "acltoownermode: ma_user after: %04o", ma->ma_user);
 
-    return;
+    LOG(log_maxdebug, logtype_afpd, "resulting user maccess: 0x%02x", ma->ma_user);
+
+EC_CLEANUP:
+    EC_EXIT;
 }
 
-/*
-  We're being called at the end of afp_createdir. We're (hopefully) inside dir
-  and ".AppleDouble" and ".AppleDouble/.Parent" should have already been created.
-  We then inherit any explicit ACE from "." to ".AppleDouble" and ".AppleDouble/.Parent".
-  FIXME: add to VFS layer ?
-*/
-void addir_inherit_acl(const struct vol *vol)
+/*!
+ * Check whether a volume supports ACLs
+ *
+ * @param vol  (r) volume
+ *
+ * @returns        0 if not, 1 if yes
+ */
+int check_vol_acl_support(const struct vol *vol)
 {
-    ace_t *diraces = NULL, *adaces = NULL, *combinedaces = NULL;
-    int diracecount, adacecount;
-
-    LOG(log_debug9, logtype_afpd, "addir_inherit_acl: BEGIN");
-
-    /* Check if ACLs are enabled for the volume */
-    if (vol->v_flags & AFPVOL_ACLS) {
-
-        if ((diracecount = get_nfsv4_acl(".", &diraces)) <= 0)
-            goto cleanup;
-        /* Remove any trivial ACE from "." */
-        if ((diracecount = strip_trivial_aces(&diraces, diracecount)) <= 0)
-            goto cleanup;
-
-        /*
-          Inherit to ".AppleDouble"
-        */
-
-        if ((adacecount = get_nfsv4_acl(".AppleDouble", &adaces)) <= 0)
-            goto cleanup;
-        /* Remove any non-trivial ACE from ".AppleDouble" */
-        if ((adacecount = strip_nontrivial_aces(&adaces, adacecount)) <= 0)
-            goto cleanup;
-
-        /* Combine ACEs */
-        if ((combinedaces = concat_aces(diraces, diracecount, adaces, adacecount)) == NULL)
-            goto cleanup;
-
-        /* Now set new acl */
-        if ((acl(".AppleDouble", ACE_SETACL, diracecount + adacecount, combinedaces)) != 0)
-            LOG(log_error, logtype_afpd, "addir_inherit_acl: acl: %s", strerror(errno));
-
-        free(adaces);
-        adaces = NULL;
-        free(combinedaces);
-        combinedaces = NULL;
-
-        /*
-          Inherit to ".AppleDouble/.Parent"
-        */
+    int ret = 1;
 
-        if ((adacecount = get_nfsv4_acl(".AppleDouble/.Parent", &adaces)) <= 0)
-            goto cleanup;
-        if ((adacecount = strip_nontrivial_aces(&adaces, adacecount)) <= 0)
-            goto cleanup;
-
-        /* Combine ACEs */
-        if ((combinedaces = concat_aces(diraces, diracecount, adaces, adacecount)) == NULL)
-            goto cleanup;
-
-        /* Now set new acl */
-        if ((acl(".AppleDouble/.Parent", ACE_SETACL, diracecount + adacecount, combinedaces)) != 0)
-            LOG(log_error, logtype_afpd, "addir_inherit_acl: acl: %s", strerror(errno));
-
-
-    }
+#ifdef HAVE_SOLARIS_ACLS
+    ace_t *aces = NULL;
+    if (get_nfsv4_acl(vol->v_path, &aces) == -1)
+        ret = 0;
+#endif
+#ifdef HAVE_POSIX_ACLS
+    acl_t acl = NULL;
+    if ((acl = acl_get_file(vol->v_path, ACL_TYPE_ACCESS)) == NULL)
+        ret = 0;
+#endif
 
-cleanup:
-    LOG(log_debug9, logtype_afpd, "addir_inherit_acl: END");
+#ifdef HAVE_SOLARIS_ACLS
+    if (aces) free(aces);
+#endif
+#ifdef HAVE_POSIX_ACLS
+    if (acl) acl_free(acl);
+#endif /* HAVE_POSIX_ACLS */
 
-    free(diraces);
-    free(adaces);
-    free(combinedaces);
+    LOG(log_debug, logtype_afpd, "Volume \"%s\" ACL support: %s",
+        vol->v_path, ret ? "yes" : "no");
+    return ret;
 }
index a1b1e52d4da85a4fcbb08c34f5537905a911c7b6..c89fe9c8f3c5fe0ee8df275a5e9d8c8b3dd9d9f6 100644 (file)
@@ -1,5 +1,4 @@
 /*
-   $Id: acls.h,v 1.3 2009-11-20 17:45:47 franklahm Exp $
    Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
 
    This program is free software; you can redistribute it and/or modify
 #ifndef AFPD_ACLS_H 
 #define AFPD_ACLS_H
 
+#ifdef HAVE_SOLARIS_ACLS
 #include <sys/acl.h>
-#include <atalk/uuid.h>                /* for uuid_t */
+#endif
+
+#include <atalk/uuid.h>                /* for atalk_uuid_t */
 
 /*
  * This is what Apple says about ACL flags in sys/kauth.h:
  * the wire! We will ignore and spoil em.
  */
 
+#ifdef HAVE_SOLARIS_ACLS
 /* Some stuff for the handling of NFSv4 ACLs */
 #define ACE_TRIVIAL (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)
+#endif /* HAVE_SOLARIS_ACLS */
 
 /* FPGet|Set Bitmap */
 enum {
@@ -53,13 +57,13 @@ enum {
 
 /* ACE Flags */
 #define DARWIN_ACE_FLAGS_KINDMASK           0xf
-#define DARWIN_ACE_FLAGS_PERMIT             (1<<0)
-#define DARWIN_ACE_FLAGS_DENY               (1<<1)
-#define DARWIN_ACE_FLAGS_INHERITED          (1<<4)
-#define DARWIN_ACE_FLAGS_FILE_INHERIT       (1<<5)
-#define DARWIN_ACE_FLAGS_DIRECTORY_INHERIT  (1<<6)
-#define DARWIN_ACE_FLAGS_LIMIT_INHERIT      (1<<7)
-#define DARWIN_ACE_FLAGS_ONLY_INHERIT       (1<<8)
+#define DARWIN_ACE_FLAGS_PERMIT             (1<<0) /* 0x00000001 */
+#define DARWIN_ACE_FLAGS_DENY               (1<<1) /* 0x00000002 */
+#define DARWIN_ACE_FLAGS_INHERITED          (1<<4) /* 0x00000010 */
+#define DARWIN_ACE_FLAGS_FILE_INHERIT       (1<<5) /* 0x00000020 */
+#define DARWIN_ACE_FLAGS_DIRECTORY_INHERIT  (1<<6) /* 0x00000040 */
+#define DARWIN_ACE_FLAGS_LIMIT_INHERIT      (1<<7) /* 0x00000080 */
+#define DARWIN_ACE_FLAGS_ONLY_INHERIT       (1<<8) /* 0x00000100 */
 
 /* All flag bits controlling ACE inheritance */
 #define DARWIN_ACE_INHERIT_CONTROL_FLAGS \
@@ -89,7 +93,7 @@ enum {
 
 /* Access Control List Entry (ACE) */
 typedef struct {
-    uuid_t      darwin_ace_uuid;
+    atalk_uuid_t      darwin_ace_uuid;
     uint32_t    darwin_ace_flags;
     uint32_t    darwin_ace_rights;
 } darwin_ace_t;
@@ -108,4 +112,7 @@ int afp_setacl (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf,  size_t *rb
 /* Parse afp_ldap.conf */
 extern int acl_ldap_readconfig(char *name);
 
+/* Misc funcs */
+extern int acltoownermode(char *path, struct stat *st, struct maccess *ma);
+extern int check_vol_acl_support(const struct vol *vol);
 #endif
diff --git a/etc/afpd/afp_avahi.c b/etc/afpd/afp_avahi.c
new file mode 100644 (file)
index 0000000..a06c7be
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * Author:  Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose: Avahi based Zeroconf support
+ * Docs:    http://avahi.org/download/doxygen/
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_AVAHI
+
+#include <unistd.h>
+
+#include <avahi-common/strlst.h>
+
+#include <atalk/logger.h>
+#include <atalk/util.h>
+#include <atalk/dsi.h>
+#include <atalk/unicode.h>
+
+#include "afp_avahi.h"
+#include "afp_config.h"
+#include "volume.h"
+
+/*****************************************************************
+ * Global variables
+ *****************************************************************/
+struct context *ctx = NULL;
+
+/*****************************************************************
+ * Private functions
+ *****************************************************************/
+
+static void publish_reply(AvahiEntryGroup *g,
+                          AvahiEntryGroupState state,
+                          void *userdata);
+
+/*
+ * This function tries to register the AFP DNS
+ * SRV service type.
+ */
+static void register_stuff(void) {
+    uint port;
+    const AFPConfig *config;
+    const struct vol *volume;
+    DSI *dsi;
+    char name[MAXINSTANCENAMELEN+1];
+    AvahiStringList *strlist = NULL;
+    char tmpname[256];
+
+    assert(ctx->client);
+
+    if (!ctx->group) {
+        if (!(ctx->group = avahi_entry_group_new(ctx->client, publish_reply, ctx))) {
+            LOG(log_error, logtype_afpd, "Failed to create entry group: %s",
+                avahi_strerror(avahi_client_errno(ctx->client)));
+            goto fail;
+        }
+    }
+
+    if (avahi_entry_group_is_empty(ctx->group)) {
+        /* Register our service */
+
+        /* Build AFP volumes list */
+        int i = 0;
+        strlist = avahi_string_list_add_printf(strlist, "sys=waMa=0,adVF=0x100");
+               
+        for (volume = getvolumes(); volume; volume = volume->v_next) {
+
+            if (convert_string(CH_UCS2, CH_UTF8_MAC, volume->v_name, -1, tmpname, 255) <= 0) {
+                LOG ( log_error, logtype_afpd, "Could not set Zeroconf volume name for TimeMachine");
+                goto fail;
+            }
+
+            if (volume->v_flags & AFPVOL_TM) {
+                if (volume->v_uuid) {
+                    LOG(log_info, logtype_afpd, "Registering volume '%s' with UUID: '%s' for TimeMachine",
+                        volume->v_localname, volume->v_uuid);
+                    strlist = avahi_string_list_add_printf(strlist, "dk%u=adVN=%s,adVF=0xa1,adVU=%s",
+                                                           i++, tmpname, volume->v_uuid);
+                } else {
+                    LOG(log_warning, logtype_afpd, "Registering volume '%s' for TimeMachine. But UUID is invalid.",
+                        volume->v_localname);
+                    strlist = avahi_string_list_add_printf(strlist, "dk%u=adVN=%s,adVF=0xa1",
+                                                           i++, tmpname);
+                }      
+            }
+        }
+
+        /* AFP server */
+        for (config = ctx->configs; config; config = config->next) {
+
+            dsi = (DSI *)config->obj.handle;
+            port = getip_port((struct sockaddr *)&dsi->server);
+
+            if (convert_string(config->obj.options.unixcharset,
+                               CH_UTF8,
+                               config->obj.options.server ?
+                               config->obj.options.server :
+                               config->obj.options.hostname,
+                               -1,
+                               name,
+                               MAXINSTANCENAMELEN) <= 0) {
+                LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name");
+                goto fail;
+            }
+            if ((dsi->bonjourname = strdup(name)) == NULL) {
+                LOG(log_error, logtype_afpd, "Could not set Zeroconf instance name");
+                goto fail;
+
+            }
+            LOG(log_info, logtype_afpd, "Registering server '%s' with with Bonjour",
+                dsi->bonjourname);
+
+            if (avahi_entry_group_add_service(ctx->group,
+                                              AVAHI_IF_UNSPEC,
+                                              AVAHI_PROTO_UNSPEC,
+                                              0,
+                                              dsi->bonjourname,
+                                              AFP_DNS_SERVICE_TYPE,
+                                              NULL,
+                                              NULL,
+                                              port,
+                                              NULL) < 0) {
+                LOG(log_error, logtype_afpd, "Failed to add service: %s",
+                    avahi_strerror(avahi_client_errno(ctx->client)));
+                goto fail;
+            }
+
+            if (i && avahi_entry_group_add_service_strlst(ctx->group,
+                                                          AVAHI_IF_UNSPEC,
+                                                          AVAHI_PROTO_UNSPEC,
+                                                          0,
+                                                          dsi->bonjourname,
+                                                          ADISK_SERVICE_TYPE,
+                                                          NULL,
+                                                          NULL,
+                                                          9, /* discard */
+                                                          strlist) < 0) {
+                LOG(log_error, logtype_afpd, "Failed to add service: %s",
+                    avahi_strerror(avahi_client_errno(ctx->client)));
+                goto fail;
+            }  /* if */
+        }      /* for config*/
+
+        if (avahi_entry_group_commit(ctx->group) < 0) {
+            LOG(log_error, logtype_afpd, "Failed to commit entry group: %s",
+                avahi_strerror(avahi_client_errno(ctx->client)));
+            goto fail;
+        }
+
+    }  /* if avahi_entry_group_is_empty*/
+
+    return;
+
+fail:
+    avahi_client_free (ctx->client);
+    avahi_threaded_poll_quit(ctx->threaded_poll);
+}
+
+/* Called when publishing of service data completes */
+static void publish_reply(AvahiEntryGroup *g,
+                          AvahiEntryGroupState state,
+                          AVAHI_GCC_UNUSED void *userdata)
+{
+    assert(ctx->group == NULL || g == ctx->group);
+
+    switch (state) {
+
+    case AVAHI_ENTRY_GROUP_ESTABLISHED :
+        /* The entry group has been established successfully */
+        LOG(log_debug, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_ESTABLISHED");
+        break;
+
+    case AVAHI_ENTRY_GROUP_COLLISION:
+        /* With multiple names there's no way to know which one collided */
+        LOG(log_error, logtype_afpd, "publish_reply: AVAHI_ENTRY_GROUP_COLLISION",
+            avahi_strerror(avahi_client_errno(ctx->client)));
+        avahi_client_free(avahi_entry_group_get_client(g));
+        avahi_threaded_poll_quit(ctx->threaded_poll);
+        break;
+               
+    case AVAHI_ENTRY_GROUP_FAILURE:
+        LOG(log_error, logtype_afpd, "Failed to register service: %s",
+            avahi_strerror(avahi_client_errno(ctx->client)));
+        avahi_client_free(avahi_entry_group_get_client(g));
+        avahi_threaded_poll_quit(ctx->threaded_poll);
+        break;
+
+    case AVAHI_ENTRY_GROUP_UNCOMMITED:
+        break;
+    case AVAHI_ENTRY_GROUP_REGISTERING:
+        break;
+    }
+}
+
+static void client_callback(AvahiClient *client,
+                            AvahiClientState state,
+                            void *userdata)
+{
+    ctx->client = client;
+
+    switch (state) {
+    case AVAHI_CLIENT_S_RUNNING:
+        /* The server has startup successfully and registered its host
+         * name on the network, so it's time to create our services */
+        if (!ctx->group)
+            register_stuff();
+        break;
+
+    case AVAHI_CLIENT_S_COLLISION:
+        if (ctx->group)
+            avahi_entry_group_reset(ctx->group);
+        break;
+
+    case AVAHI_CLIENT_FAILURE: {
+        if (avahi_client_errno(client) == AVAHI_ERR_DISCONNECTED) {
+            int error;
+
+            avahi_client_free(ctx->client);
+            ctx->client = NULL;
+            ctx->group = NULL;
+
+            /* Reconnect to the server */
+            if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
+                                                 AVAHI_CLIENT_NO_FAIL,
+                                                 client_callback,
+                                                 ctx,
+                                                 &error))) {
+
+                LOG(log_error, logtype_afpd, "Failed to contact server: %s",
+                    avahi_strerror(error));
+
+                avahi_client_free (ctx->client);
+                avahi_threaded_poll_quit(ctx->threaded_poll);
+            }
+
+        } else {
+            LOG(log_error, logtype_afpd, "Client failure: %s",
+                avahi_strerror(avahi_client_errno(client)));
+            avahi_client_free (ctx->client);
+            avahi_threaded_poll_quit(ctx->threaded_poll);
+        }
+        break;
+    }
+
+    case AVAHI_CLIENT_S_REGISTERING:
+        break;
+    case AVAHI_CLIENT_CONNECTING:
+        break;
+    }
+}
+
+/************************************************************************
+ * Public funcions
+ ************************************************************************/
+
+/*
+ * Tries to setup the Zeroconf thread and any
+ * neccessary config setting.
+ */
+void av_zeroconf_setup(const AFPConfig *configs) {
+    int error;
+
+    /* initialize the struct that holds our config settings. */
+    if (ctx) {
+        LOG(log_debug, logtype_afpd, "Resetting zeroconf records");
+        avahi_entry_group_reset(ctx->group);
+    } else {
+        ctx = calloc(1, sizeof(struct context));
+        ctx->configs = configs;
+        assert(ctx);
+    }
+
+/* first of all we need to initialize our threading env */
+    if (!(ctx->threaded_poll = avahi_threaded_poll_new())) {
+        goto fail;
+    }
+
+/* now we need to acquire a client */
+    if (!(ctx->client = avahi_client_new(avahi_threaded_poll_get(ctx->threaded_poll),
+                                         AVAHI_CLIENT_NO_FAIL,
+                                         client_callback,
+                                         NULL,
+                                         &error))) {
+        LOG(log_error, logtype_afpd, "Failed to create client object: %s",
+            avahi_strerror(avahi_client_errno(ctx->client)));
+        goto fail;
+    }
+
+    return;
+
+fail:
+    if (ctx)
+        av_zeroconf_unregister();
+
+    return;
+}
+
+/*
+ * This function finally runs the loop impl.
+ */
+int av_zeroconf_run(void) {
+    /* Finally, start the event loop thread */
+    if (avahi_threaded_poll_start(ctx->threaded_poll) < 0) {
+        LOG(log_error, logtype_afpd, "Failed to create thread: %s",
+            avahi_strerror(avahi_client_errno(ctx->client)));
+        goto fail;
+    } else {
+        LOG(log_info, logtype_afpd, "Successfully started avahi loop.");
+    }
+
+    ctx->thread_running = 1;
+    return 0;
+
+fail:
+    if (ctx)
+        av_zeroconf_unregister();
+
+    return -1;
+}
+
+/*
+ * Tries to shutdown this loop impl.
+ * Call this function from outside this thread.
+ */
+void av_zeroconf_shutdown() {
+    /* Call this when the app shuts down */
+    avahi_threaded_poll_stop(ctx->threaded_poll);
+    avahi_client_free(ctx->client);
+    avahi_threaded_poll_free(ctx->threaded_poll);
+    free(ctx);
+    ctx = NULL;
+}
+
+/*
+ * Tries to shutdown this loop impl.
+ * Call this function from inside this thread.
+ */
+int av_zeroconf_unregister() {
+    if (ctx->thread_running) {
+        /* First, block the event loop */
+        avahi_threaded_poll_lock(ctx->threaded_poll);
+
+        /* Than, do your stuff */
+        avahi_threaded_poll_quit(ctx->threaded_poll);
+
+        /* Finally, unblock the event loop */
+        avahi_threaded_poll_unlock(ctx->threaded_poll);
+        ctx->thread_running = 0;
+    }
+
+    if (ctx->client)
+        avahi_client_free(ctx->client);
+
+    if (ctx->threaded_poll)
+        avahi_threaded_poll_free(ctx->threaded_poll);
+
+    free(ctx);
+    ctx = NULL;
+
+    return 0;
+}
+
+#endif /* USE_AVAHI */
+
diff --git a/etc/afpd/afp_avahi.h b/etc/afpd/afp_avahi.h
new file mode 100644 (file)
index 0000000..2c6ca70
--- /dev/null
@@ -0,0 +1,48 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
+/*
+ * Author:  Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose: Avahi based Zeroconf support
+ * Docs:    http://avahi.org/download/doxygen/
+ *
+ */
+
+#ifndef AFPD_AVAHI_H
+#define AFPD_AVAHI_H
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include <avahi-client/client.h>
+#include <avahi-client/publish.h>
+#include <avahi-common/alternative.h>
+#include <avahi-common/thread-watch.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+#include <atalk/logger.h>
+
+#include "afp_config.h"
+
+#define AFP_DNS_SERVICE_TYPE "_afpovertcp._tcp"
+#define ADISK_SERVICE_TYPE "_adisk._tcp"
+
+#define MAXINSTANCENAMELEN 63
+
+struct context {
+       /* Avahi stuff */
+  int               thread_running;
+  AvahiThreadedPoll *threaded_poll;
+  AvahiClient       *client;
+  AvahiEntryGroup   *group;
+       /* Netatalk stuff */
+       const AFPConfig   *configs;
+};
+
+/* prototype definitions */
+void av_zeroconf_setup(const AFPConfig *configs);
+int av_zeroconf_run(void);
+int av_zeroconf_unregister(void);
+void av_zeroconf_shutdown(void);
+
+#endif   /* AFPD_AVAHI_H */
index 6381479d0ddc6a30b46ba069849d582be927f731..7f5fd1f1fc9e32d10f3adc16c85da1cfdfdae4b6 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-
-/* STDC check */
-#if STDC_HEADERS
 #include <string.h>
-#else /* STDC_HEADERS */
-#ifndef HAVE_STRCHR
-#define strchr index
-#define strrchr index
-#endif /* HAVE_STRCHR */
-char *strchr (), *strrchr ();
-#ifndef HAVE_MEMCPY
-#define memcpy(d,s,n) bcopy ((s), (d), (n))
-#define memmove(d,s,n) bcopy ((s), (d), (n))
-#endif /* ! HAVE_MEMCPY */
-#endif /* STDC_HEADERS */
-
-#ifdef HAVE_UNISTD_H
 #include <unistd.h>
-#endif /* HAVE_UNISTD_H */
 #include <ctype.h>
-#include <atalk/logger.h>
-#include <atalk/util.h>
-
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
+#ifdef USE_SRVLOC
+#include <slp.h>
+#endif /* USE_SRVLOC */
+
+#include <atalk/logger.h>
+#include <atalk/util.h>
 #include <atalk/dsi.h>
 #include <atalk/atp.h>
 #include <atalk/asp.h>
@@ -45,10 +31,8 @@ char *strchr (), *strrchr ();
 #include <atalk/afp.h>
 #include <atalk/compat.h>
 #include <atalk/server_child.h>
-#ifdef USE_SRVLOC
-#include <slp.h>
-#endif /* USE_SRVLOC */
-#ifdef HAVE_NFSv4_ACLS
+
+#ifdef HAVE_LDAP
 #include <atalk/ldapconfig.h>
 #endif
 
@@ -56,6 +40,8 @@ char *strchr (), *strrchr ();
 #include "afp_config.h"
 #include "uam_auth.h"
 #include "status.h"
+#include "volume.h"
+#include "afp_zeroconf.h"
 
 #define LINESIZE 1024  
 
@@ -94,6 +80,9 @@ void configfree(AFPConfig *configs, const AFPConfig *config)
         }
         free(p);
     }
+
+    /* the master loaded the volumes for zeroconf, get rid of that */
+    unload_volumes_and_extmap();
 }
 
 #ifdef USE_SRVLOC
@@ -155,9 +144,9 @@ static char * srvloc_encode(const struct afp_options *options, const char *name)
 }
 #endif /* USE_SRVLOC */
 
-#ifdef USE_SRVLOC
 static void dsi_cleanup(const AFPConfig *config)
 {
+#ifdef USE_SRVLOC
     SLPError err;
     SLPError callbackerr;
     SLPHandle hslp;
@@ -190,8 +179,8 @@ static void dsi_cleanup(const AFPConfig *config)
 srvloc_dereg_err:
     dsi->srvloc_url[0] = '\0';
     SLPClose(hslp);
-}
 #endif /* USE_SRVLOC */
+}
 
 #ifndef NO_DDP
 static void asp_cleanup(const AFPConfig *config)
@@ -225,25 +214,30 @@ static int asp_start(AFPConfig *config, AFPConfig *configs,
 }
 #endif /* no afp/asp */
 
-static int dsi_start(AFPConfig *config, AFPConfig *configs,
-                     server_child *server_children)
+static afp_child_t *dsi_start(AFPConfig *config, AFPConfig *configs,
+                              server_child *server_children)
 {
-    DSI *dsi;
+    DSI *dsi = config->obj.handle;
+    afp_child_t *child = NULL;
 
-    if (!(dsi = dsi_getsession(config->obj.handle, server_children,
-                               config->obj.options.tickleval))) {
-        LOG(log_error, logtype_afpd, "main: dsi_getsession: %s", strerror(errno) );
-        exit( EXITERR_CLNT );
+    if (!(child = dsi_getsession(dsi,
+                                 server_children,
+                                 config->obj.options.tickleval))) {
+        LOG(log_error, logtype_afpd, "dsi_start: session error: %s", strerror(errno));
+        return NULL;
     }
 
     /* we've forked. */
-    if (dsi->child) {
+    if (parent_or_child == 1) {
         configfree(configs, config);
+        config->obj.ipc_fd = child->ipc_fds[1];
+        close(child->ipc_fds[0]); /* Close parent IPC fd */
+        free(child);
         afp_over_dsi(&config->obj); /* start a session */
         exit (0);
     }
 
-    return 0;
+    return child;
 }
 
 #ifndef NO_DDP
@@ -368,12 +362,13 @@ static AFPConfig *DSIConfigInit(const struct afp_options *options,
         free(config);
         return NULL;
     }
+    dsi->dsireadbuf = options->dsireadbuf;
 
     if (options->flags & OPTION_PROXY) {
-        LOG(log_info, logtype_afpd, "AFP/TCP proxy initialized for %s:%d (%s)",
+        LOG(log_note, logtype_afpd, "AFP/TCP proxy initialized for %s:%d (%s)",
             getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
     } else {
-        LOG(log_info, logtype_afpd, "AFP/TCP started, advertising %s:%d (%s)",
+        LOG(log_note, logtype_afpd, "AFP/TCP started, advertising %s:%d (%s)",
             getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
     }
 
@@ -453,7 +448,6 @@ srvloc_reg_err:
     }
 #endif /* USE_SRVLOC */
 
-
     config->fd = dsi->serversock;
     config->obj.handle = dsi;
     config->obj.config = config;
@@ -476,7 +470,7 @@ srvloc_reg_err:
 /* allocate server configurations. this should really store the last
  * entry in config->last or something like that. that would make
  * supporting multiple dsi transports easier. */
-static AFPConfig *AFPConfigInit(const struct afp_options *options,
+static AFPConfig *AFPConfigInit(struct afp_options *options,
                                 const struct afp_options *defoptions)
 {
     AFPConfig *config = NULL, *next = NULL;
@@ -543,13 +537,6 @@ AFPConfig *configinit(struct afp_options *cmdline)
     struct afp_options options;
     AFPConfig *config=NULL, *first = NULL; 
 
-#ifdef HAVE_NFSv4_ACLS
-    /* Parse afp_ldap.conf first so we can set the uuid option */
-    LOG(log_debug, logtype_afpd, "Start parsing afp_ldap.conf");
-    acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
-    LOG(log_debug, logtype_afpd, "Finished parsing afp_ldap.conf");
-#endif
-
     /* if config file doesn't exist, load defaults */
     if ((fp = fopen(cmdline->configfile, "r")) == NULL)
     {
@@ -558,8 +545,6 @@ AFPConfig *configinit(struct afp_options *cmdline)
         return AFPConfigInit(cmdline, cmdline);
     }
 
-    LOG(log_debug, logtype_afpd, "Loading ConfigFile"); 
-
     /* scan in the configuration file */
     len = 0;
     while (!feof(fp)) {
@@ -585,13 +570,7 @@ AFPConfig *configinit(struct afp_options *cmdline)
         if (!afp_options_parseline(p, &options))
             continue;
 
-#ifdef HAVE_NFSv4_ACLS
-       /* Enable UUID support if LDAP config is complete */
-       if (ldap_config_valid)
-           options.flags |= OPTION_UUID;
-#endif
-
-        /* this should really get a head and a tail to simplify things. */
+        /* AFPConfigInit can return two linked configs due to DSI and ASP */
         if (!first) {
             if ((first = AFPConfigInit(&options, cmdline)))
                 config = first->next ? first->next : first;
@@ -600,11 +579,22 @@ AFPConfig *configinit(struct afp_options *cmdline)
         }
     }
 
+#ifdef HAVE_LDAP
+    /* Parse afp_ldap.conf */
+    acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
+#endif /* HAVE_LDAP */
+
     LOG(log_debug, logtype_afpd, "Finished parsing Config File");
     fclose(fp);
 
     if (!have_option)
         first = AFPConfigInit(cmdline, cmdline);
 
+    /* Now register with zeroconf, we also need the volumes for that */
+    if (! (first->obj.options.flags & OPTION_NOZEROCONF)) {
+        load_volumes(&first->obj);
+        zeroconf_register(first);
+    }
+
     return first;
 }
index 1a0d08ef8859426a5e7fa8d113046d95308ff2c3..ba800ce3aa45c5b547b296ea0e650ebf9a48d6f7 100644 (file)
@@ -19,7 +19,7 @@ typedef struct AFPConfig {
     unsigned char *optcount;
     char status[1400];
     const void *defoptions, *signature;
-    int (*server_start) (struct AFPConfig *, struct AFPConfig *,
+    afp_child_t *(*server_start) (struct AFPConfig *, struct AFPConfig *,
                              server_child *);
     void (*server_cleanup) (const struct AFPConfig *);
     struct AFPConfig *next;
index 8bcfb9c6399ee9aeeeb7aeb0eeca131b229f3073..0a735ea4d2ae1a0946b63d0ece3cdea8756547f8 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: afp_dsi.c,v 1.53 2010-03-30 12:55:26 franklahm Exp $
- *
  * Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -28,6 +26,7 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <atalk/logger.h>
+#include <setjmp.h>
 
 #include <atalk/dsi.h>
 #include <atalk/compat.h>
 #include "switch.h"
 #include "auth.h"
 #include "fork.h"
+#include "dircache.h"
 
 #ifdef FORCE_UIDGID
 #warning UIDGID
 #include "uid.h"
 #endif /* FORCE_UIDGID */
 
-#define CHILD_DIE         (1 << 0)
-#define CHILD_RUNNING     (1 << 1)
-#define CHILD_SLEEPING    (1 << 2)
-#define CHILD_DATA        (1 << 3)
-
 /* 
  * We generally pass this from afp_over_dsi to all afp_* funcs, so it should already be
  * available everywhere. Unfortunately some funcs (eg acltoownermode) need acces to it
  */
 AFPObj *AFPobj = NULL;
 
-static struct {
-    AFPObj *obj;
-    unsigned char flags;
-    int tickle;
-} child;
+typedef struct {
+    uint16_t DSIreqID;
+    uint8_t  AFPcommand;
+    uint32_t result;
+} rc_elem_t;
 
+/*
+ * AFP replay cache:
+ * - fix sized array
+ * - indexed just by taking DSIreqID mod REPLAYCACHE_SIZE
+ */
+static rc_elem_t replaycache[REPLAYCACHE_SIZE];
 
+static sigjmp_buf recon_jmp;
 static void afp_dsi_close(AFPObj *obj)
 {
     DSI *dsi = obj->handle;
 
+    close(obj->ipc_fd);
+    obj->ipc_fd = -1;
+
     /* we may have been called from a signal handler caught when afpd was running
      * as uid 0, that's the wrong user for volume's prexec_close scripts if any,
      * restore our login user
@@ -85,8 +90,9 @@ static void afp_dsi_close(AFPObj *obj)
     if (obj->logout)
         (*obj->logout)();
 
-    LOG(log_info, logtype_afpd, "%.2fKB read, %.2fKB written",
+    LOG(log_note, logtype_afpd, "AFP statistics: %.2f KB read, %.2f KB written",
         dsi->read_count/1024.0, dsi->write_count/1024.0);
+    log_dircache_stat();
 
     dsi_close(dsi);
 }
@@ -97,20 +103,22 @@ static void afp_dsi_close(AFPObj *obj)
  */
 static void afp_dsi_die(int sig)
 {
-static volatile int in_handler;
-    
-    if (in_handler) {
-       return;
+    DSI *dsi = (DSI *)AFPobj->handle;
+
+    if (dsi->flags & DSI_RECONINPROG) {
+        /* Primary reconnect succeeded, got SIGTERM from afpd parent */
+        dsi->flags &= ~DSI_RECONINPROG;
+        return; /* this returns to afp_disconnect */
     }
-    /* it's not atomic but we don't care because it's an exit function
-     * ie if a signal is received here, between the test and the affectation,
-     * it will not return.
-    */
-    in_handler = 1;
-
-    dsi_attention(child.obj->handle, AFPATTN_SHUTDOWN);
-    afp_dsi_close(child.obj);
-    if (sig) /* if no signal, assume dieing because logins are disabled &
+
+    if (dsi->flags & DSI_DISCONNECTED) {
+        LOG(log_note, logtype_afpd, "Disconnected session terminating");
+        exit(0);
+    }
+
+    dsi_attention(AFPobj->handle, AFPATTN_SHUTDOWN);
+    afp_dsi_close(AFPobj);
+   if (sig) /* if no signal, assume dieing because logins are disabled &
                 don't log it (maintenance mode)*/
         LOG(log_info, logtype_afpd, "Connection terminated");
     if (sig == SIGTERM || sig == SIGALRM) {
@@ -121,11 +129,55 @@ static volatile int in_handler;
     }
 }
 
-/* */
-static void afp_dsi_sleep(void)
+/* SIGURG handler (primary reconnect) */
+static void afp_dsi_transfer_session(int sig _U_)
 {
-    child.flags |= CHILD_SLEEPING;
-    dsi_sleep(child.obj->handle, 1);
+    uint16_t dsiID;
+    int socket;
+    DSI *dsi = (DSI *)AFPobj->handle;
+
+    LOG(log_debug, logtype_afpd, "afp_dsi_transfer_session: got SIGURG, trying to receive session");
+
+    if (readt(AFPobj->ipc_fd, &dsiID, 2, 0, 2) != 2) {
+        LOG(log_error, logtype_afpd, "afp_dsi_transfer_session: couldn't receive DSI id, goodbye");
+        afp_dsi_close(AFPobj);
+        exit(EXITERR_SYS);
+    }
+
+    if ((socket = recv_fd(AFPobj->ipc_fd, 1)) == -1) {
+        LOG(log_error, logtype_afpd, "afp_dsi_transfer_session: couldn't receive session fd, goodbye");
+        afp_dsi_close(AFPobj);
+        exit(EXITERR_SYS);
+    }
+
+    LOG(log_debug, logtype_afpd, "afp_dsi_transfer_session: received socket fd: %i", socket);
+
+    dsi->proto_close(dsi);
+    dsi->socket = socket;
+    dsi->flags = DSI_RECONSOCKET;
+    dsi->datalen = 0;
+    dsi->eof = dsi->start = dsi->buffer;
+    dsi->in_write = 0;
+    dsi->header.dsi_requestID = dsiID;
+    dsi->header.dsi_command = DSIFUNC_CMD;
+
+    /*
+     * The session transfer happens in the middle of FPDisconnect old session, thus we
+     * have to send the reply now.
+     */
+    if (!dsi_cmdreply(dsi, AFP_OK)) {
+        LOG(log_error, logtype_afpd, "dsi_cmdreply: %s", strerror(errno) );
+        afp_dsi_close(AFPobj);
+        exit(EXITERR_CLNT);
+    }
+
+    LOG(log_note, logtype_afpd, "afp_dsi_transfer_session: succesfull primary reconnect");
+    /* 
+     * Now returning from this signal handler return to dsi_receive which should start
+     * reading/continuing from the connected socket that was passed via the parent from
+     * another session. The parent will terminate that session.
+     */
+    siglongjmp(recon_jmp, 1);
 }
 
 /* ------------------- */
@@ -133,13 +185,13 @@ static void afp_dsi_timedown(int sig _U_)
 {
     struct sigaction   sv;
     struct itimerval   it;
-
-    child.flags |= CHILD_DIE;
+    DSI                 *dsi = (DSI *)AFPobj->handle;
+    dsi->flags |= DSI_DIE;
     /* shutdown and don't reconnect. server going down in 5 minutes. */
     setmessage("The server is going down for maintenance.");
-    if (dsi_attention(child.obj->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
+    if (dsi_attention(AFPobj->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
                   AFPATTN_MESG | AFPATTN_TIME(5)) < 0) {
-        DSI *dsi = (DSI *) child.obj->handle;
+        DSI *dsi = (DSI *)AFPobj->handle;
         dsi->down_request = 1;
     }                  
 
@@ -175,8 +227,7 @@ static void afp_dsi_timedown(int sig _U_)
 
 /* ---------------------------------
  * SIGHUP reload configuration file
- * FIXME here or we wait ?
-*/
+ */
 volatile int reload_request = 0;
 
 static void afp_dsi_reload(int sig _U_)
@@ -195,48 +246,73 @@ static void afp_dsi_debug(int sig _U_)
 }
 
 /* ---------------------- */
-#ifdef SERVERTEXT
 static void afp_dsi_getmesg (int sig _U_)
 {
-    DSI *dsi = (DSI *) child.obj->handle;
+    DSI *dsi = (DSI *)AFPobj->handle;
 
     dsi->msg_request = 1;
-    if (dsi_attention(child.obj->handle, AFPATTN_MESG | AFPATTN_TIME(5)) < 0)
+    if (dsi_attention(AFPobj->handle, AFPATTN_MESG | AFPATTN_TIME(5)) < 0)
         dsi->msg_request = 2;
 }
-#endif /* SERVERTEXT */
 
 static void alarm_handler(int sig _U_)
 {
     int err;
-    DSI *dsi = (DSI *) child.obj->handle;
+    DSI *dsi = (DSI *)AFPobj->handle;
 
-    /* we have to restart the timer because some libraries 
-     * may use alarm() */
+    /* we have to restart the timer because some libraries may use alarm() */
     setitimer(ITIMER_REAL, &dsi->timer, NULL);
 
-    /* we got some traffic from the client since the previous timer 
-     * tick. */
-    if ((child.flags & CHILD_DATA)) {
-        child.flags &= ~CHILD_DATA;
+    /* we got some traffic from the client since the previous timer tick. */
+    if ((dsi->flags & DSI_DATA)) {
+        dsi->flags &= ~DSI_DATA;
         return;
     }
 
-    /* if we're in the midst of processing something,
-       don't die. */
-    if ((child.flags & CHILD_SLEEPING) && child.tickle++ < child.obj->options.sleep) {
+    dsi->tickle++;
+    LOG(log_maxdebug, logtype_afpd, "alarm: tickles: %u, flags: %s|%s|%s|%s|%s|%s|%s|%s|%s",
+        dsi->tickle,
+        (dsi->flags & DSI_DATA) ?         "DSI_DATA" : "-",
+        (dsi->flags & DSI_RUNNING) ?      "DSI_RUNNING" : "-",
+        (dsi->flags & DSI_SLEEPING) ?     "DSI_SLEEPING" : "-",
+        (dsi->flags & DSI_EXTSLEEP) ?     "DSI_EXTSLEEP" : "-",
+        (dsi->flags & DSI_DISCONNECTED) ? "DSI_DISCONNECTED" : "-",
+        (dsi->flags & DSI_DIE) ?          "DSI_DIE" : "-",
+        (dsi->flags & DSI_NOREPLY) ?      "DSI_NOREPLY" : "-",
+        (dsi->flags & DSI_RECONSOCKET) ?  "DSI_RECONSOCKET" : "-",
+        (dsi->flags & DSI_RECONINPROG) ?  "DSI_RECONINPROG" : "-");
+
+    if (dsi->flags & DSI_SLEEPING) {
+        if (dsi->tickle > AFPobj->options.sleep) {
+            LOG(log_note, logtype_afpd, "afp_alarm: sleep time ended");
+            afp_dsi_die(EXITERR_CLNT);
+        }
         return;
     } 
-        
-    if ((child.flags & CHILD_RUNNING) || (child.tickle++ < child.obj->options.timeout)) {
-        if (!(err = pollvoltime(child.obj)))
-            err = dsi_tickle(child.obj->handle);
-        if (err <= 0) 
+
+    if (dsi->flags & DSI_DISCONNECTED) {
+        if (dsi->tickle > AFPobj->options.disconnected) {
+            LOG(log_error, logtype_afpd, "afp_alarm: reconnect timer expired, goodbye");
             afp_dsi_die(EXITERR_CLNT);
-        
-    } else { /* didn't receive a tickle. close connection */
-        LOG(log_error, logtype_afpd, "afp_alarm: child timed out");
-        afp_dsi_die(EXITERR_CLNT);
+        }
+        return;
+    }
+
+    /* if we're in the midst of processing something, don't die. */        
+    if ( !(dsi->flags & DSI_RUNNING) && (dsi->tickle >= AFPobj->options.timeout)) {
+        LOG(log_error, logtype_afpd, "afp_alarm: child timed out, entering disconnected state");
+        dsi->proto_close(dsi);
+        dsi->flags |= DSI_DISCONNECTED;
+        return;
+    }
+
+    if ((err = pollvoltime(AFPobj)) == 0)
+        LOG(log_debug, logtype_afpd, "afp_alarm: sending DSI tickle");
+        err = dsi_tickle(AFPobj->handle);
+    if (err <= 0) {
+        LOG(log_error, logtype_afpd, "afp_alarm: connection problem, entering disconnected state");
+        dsi->proto_close(dsi);
+        dsi->flags |= DSI_DISCONNECTED;
     }
 }
 
@@ -252,14 +328,14 @@ static void pending_request(DSI *dsi)
     if (dsi->msg_request) {
         if (dsi->msg_request == 2) {
             /* didn't send it in signal handler */
-            dsi_attention(child.obj->handle, AFPATTN_MESG | AFPATTN_TIME(5));
+            dsi_attention(AFPobj->handle, AFPATTN_MESG | AFPATTN_TIME(5));
         }
         dsi->msg_request = 0;
-        readmessage(child.obj);
+        readmessage(AFPobj);
     }
     if (dsi->down_request) {
         dsi->down_request = 0;
-        dsi_attention(child.obj->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
+        dsi_attention(AFPobj->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
                   AFPATTN_MESG | AFPATTN_TIME(5));
     }
 }
@@ -270,6 +346,7 @@ static void pending_request(DSI *dsi)
 void afp_over_dsi(AFPObj *obj)
 {
     DSI *dsi = (DSI *) obj->handle;
+    int rc_idx;
     u_int32_t err, cmd;
     u_int8_t function;
     struct sigaction action;
@@ -278,10 +355,7 @@ void afp_over_dsi(AFPObj *obj)
     obj->exit = afp_dsi_die;
     obj->reply = (int (*)()) dsi_cmdreply;
     obj->attention = (int (*)(void *, AFPUserBytes)) dsi_attention;
-
-    obj->sleep = afp_dsi_sleep;
-    child.obj = obj;
-    child.tickle = child.flags = 0;
+    dsi->tickle = 0;
 
     memset(&action, 0, sizeof(action));
 
@@ -292,15 +366,27 @@ void afp_over_dsi(AFPObj *obj)
     sigaddset(&action.sa_mask, SIGTERM);
     sigaddset(&action.sa_mask, SIGUSR1);
     sigaddset(&action.sa_mask, SIGINT);
-#ifdef SERVERTEXT
     sigaddset(&action.sa_mask, SIGUSR2);
-#endif    
     action.sa_flags = SA_RESTART;
     if ( sigaction( SIGHUP, &action, NULL ) < 0 ) {
         LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
         afp_dsi_die(EXITERR_SYS);
     }
 
+    /* install SIGURG */
+    action.sa_handler = afp_dsi_transfer_session;
+    sigemptyset( &action.sa_mask );
+    sigaddset(&action.sa_mask, SIGALRM);
+    sigaddset(&action.sa_mask, SIGTERM);
+    sigaddset(&action.sa_mask, SIGUSR1);
+    sigaddset(&action.sa_mask, SIGINT);
+    sigaddset(&action.sa_mask, SIGUSR2);
+    action.sa_flags = SA_RESTART;
+    if ( sigaction( SIGURG, &action, NULL ) < 0 ) {
+        LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
+        afp_dsi_die(EXITERR_SYS);
+    }
+
     /* install SIGTERM */
     action.sa_handler = afp_dsi_die;
     sigemptyset( &action.sa_mask );
@@ -308,16 +394,13 @@ void afp_over_dsi(AFPObj *obj)
     sigaddset(&action.sa_mask, SIGHUP);
     sigaddset(&action.sa_mask, SIGUSR1);
     sigaddset(&action.sa_mask, SIGINT);
-#ifdef SERVERTEXT
     sigaddset(&action.sa_mask, SIGUSR2);
-#endif    
     action.sa_flags = SA_RESTART;
     if ( sigaction( SIGTERM, &action, NULL ) < 0 ) {
         LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
         afp_dsi_die(EXITERR_SYS);
     }
 
-#ifdef SERVERTEXT
     /* Added for server message support */
     action.sa_handler = afp_dsi_getmesg;
     sigemptyset( &action.sa_mask );
@@ -331,7 +414,6 @@ void afp_over_dsi(AFPObj *obj)
         LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
         afp_dsi_die(EXITERR_SYS);
     }
-#endif /* SERVERTEXT */
 
     /*  SIGUSR1 - set down in 5 minutes  */
     action.sa_handler = afp_dsi_timedown;
@@ -340,9 +422,7 @@ void afp_over_dsi(AFPObj *obj)
     sigaddset(&action.sa_mask, SIGHUP);
     sigaddset(&action.sa_mask, SIGTERM);
     sigaddset(&action.sa_mask, SIGINT);
-#ifdef SERVERTEXT
     sigaddset(&action.sa_mask, SIGUSR2);
-#endif    
     action.sa_flags = SA_RESTART;
     if ( sigaction( SIGUSR1, &action, NULL) < 0 ) {
         LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
@@ -366,9 +446,7 @@ void afp_over_dsi(AFPObj *obj)
     sigaddset(&action.sa_mask, SIGTERM);
     sigaddset(&action.sa_mask, SIGUSR1);
     sigaddset(&action.sa_mask, SIGINT);
-#ifdef SERVERTEXT
     sigaddset(&action.sa_mask, SIGUSR2);
-#endif    
     action.sa_flags = SA_RESTART;
     if ((sigaction(SIGALRM, &action, NULL) < 0) ||
             (setitimer(ITIMER_REAL, &dsi->timer, NULL) < 0)) {
@@ -376,48 +454,109 @@ void afp_over_dsi(AFPObj *obj)
     }
 #endif /* DEBUGGING */
 
+    if (dircache_init(obj->options.dircachesize) != 0)
+        afp_dsi_die(EXITERR_SYS);
+
+    /* set TCP snd/rcv buf */
+    if (obj->options.tcp_rcvbuf) {
+        if (setsockopt(dsi->socket,
+                       SOL_SOCKET,
+                       SO_RCVBUF,
+                       &obj->options.tcp_rcvbuf,
+                       sizeof(obj->options.tcp_rcvbuf)) != 0) {
+            LOG(log_error, logtype_dsi, "afp_over_dsi: setsockopt(SO_RCVBUF): %s", strerror(errno));
+        }
+    }
+    if (obj->options.tcp_sndbuf) {
+        if (setsockopt(dsi->socket,
+                       SOL_SOCKET,
+                       SO_SNDBUF,
+                       &obj->options.tcp_sndbuf,
+                       sizeof(obj->options.tcp_sndbuf)) != 0) {
+            LOG(log_error, logtype_dsi, "afp_over_dsi: setsockopt(SO_SNDBUF): %s", strerror(errno));
+        }
+    }
+
     /* get stuck here until the end */
-    while ((cmd = dsi_receive(dsi))) {
-        child.tickle = 0;
-        child.flags &= ~CHILD_SLEEPING;
-        dsi_sleep(dsi, 0); /* wake up */
+    while (1) {
+        if (sigsetjmp(recon_jmp, 1) != 0)
+            /* returning from SIGALARM handler for a primary reconnect */
+            continue;
+
+        /* Blocking read on the network socket */
+        cmd = dsi_receive(dsi);
+
+        if (cmd == 0) {
+            /* cmd == 0 is the error condition */
+            if (dsi->flags & DSI_RECONSOCKET) {
+                /* we just got a reconnect so we immediately try again to receive on the new fd */
+                dsi->flags &= ~DSI_RECONSOCKET;
+                continue;
+            }
+            /* Some error on the client connection, enter disconnected state */
+            dsi->flags |= DSI_DISCONNECTED;
+
+            /* the client sometimes logs out (afp_logout) but doesn't close the DSI session */
+            if (dsi->flags & DSI_AFP_LOGGED_OUT) {
+                afp_dsi_close(obj);
+                exit(0);
+            }
+
+            pause(); /* gets interrupted by SIGALARM or SIGURG tickle */
+            continue; /* continue receiving until disconnect timer expires
+                       * or a primary reconnect succeeds  */
+        }
+
+        if (!(dsi->flags & DSI_EXTSLEEP) && (dsi->flags & DSI_SLEEPING)) {
+            LOG(log_debug, logtype_afpd, "afp_over_dsi: got data, ending normal sleep");
+            dsi->flags &= ~DSI_SLEEPING;
+            dsi->tickle = 0;
+        }
 
         if (reload_request) {
             reload_request = 0;
-            load_volumes(child.obj);
+            load_volumes(AFPobj);
+            dircache_dump();
+            log_dircache_stat();
         }
 
+        /* The first SIGINT enables debugging, the next restores the config */
         if (debug_request) {
-            char logstr[50];
+            static int debugging = 0;
             debug_request = 0;
 
-            /* The first SIGINT enables debugging, the second one kills us */
-            action.sa_handler = afp_dsi_die;
-            sigfillset( &action.sa_mask );
-            action.sa_flags = SA_RESTART;
-            if ( sigaction( SIGINT, &action, NULL ) < 0 ) {
-                LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
-                afp_dsi_die(EXITERR_SYS);
+            if (debugging) {
+                if (obj->options.logconfig)
+                    setuplog(obj->options.logconfig);
+                else
+                    setuplog("default log_note");
+                debugging = 0;
+            } else {
+                char logstr[50];
+                debugging = 1;
+                sprintf(logstr, "default log_maxdebug /tmp/afpd.%u.XXXXXX", getpid());
+                setuplog(logstr);
             }
-
-            sprintf(logstr, "default log_maxdebug /tmp/afpd.%u.XXXXXX", getpid());
-            setuplog(logstr);
         }
 
-        if (cmd == DSIFUNC_TICKLE) {
-            /* timer is not every 30 seconds anymore, so we don't get killed on the client side. */
-            if ((child.flags & CHILD_DIE))
-                dsi_tickle(dsi);
-            pending_request(dsi);
-            continue;
-        } 
 
-        child.flags |= CHILD_DATA;
+        dsi->flags |= DSI_DATA;
+        dsi->tickle = 0;
+
         switch(cmd) {
+
         case DSIFUNC_CLOSE:
+            LOG(log_debug, logtype_afpd, "DSI: close session request");
             afp_dsi_close(obj);
-            LOG(log_info, logtype_afpd, "done");
-            return;
+            LOG(log_note, logtype_afpd, "done");
+            exit(0);
+
+        case DSIFUNC_TICKLE:
+            dsi->flags &= ~DSI_DATA; /* thats no data in the sense we use it in alarm_handler */
+            LOG(log_debug, logtype_afpd, "DSI: client tickle");
+            /* timer is not every 30 seconds anymore, so we don't get killed on the client side. */
+            if ((dsi->flags & DSI_DIE))
+                dsi_tickle(dsi);
             break;
 
         case DSIFUNC_CMD:
@@ -432,41 +571,61 @@ void afp_over_dsi(AFPObj *obj)
 
             function = (u_char) dsi->commands[0];
 
-            /* send off an afp command. in a couple cases, we take advantage
-             * of the fact that we're a stream-based protocol. */
-            if (afp_switch[function]) {
-                dsi->datalen = DSI_DATASIZ;
-                child.flags |= CHILD_RUNNING;
+            /* AFP replay cache */
+            rc_idx = dsi->clientID % REPLAYCACHE_SIZE;
+            LOG(log_debug, logtype_afpd, "DSI request ID: %u", dsi->clientID);
 
-                LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function));
+            if (replaycache[rc_idx].DSIreqID == dsi->clientID
+                && replaycache[rc_idx].AFPcommand == function) {
+                LOG(log_note, logtype_afpd, "AFP Replay Cache match: id: %u / cmd: %s",
+                    dsi->clientID, AfpNum2name(function));
+                err = replaycache[rc_idx].result;
+            /* AFP replay cache end */
+            } else {
+                /* send off an afp command. in a couple cases, we take advantage
+                 * of the fact that we're a stream-based protocol. */
+                if (afp_switch[function]) {
+                    dsi->datalen = DSI_DATASIZ;
+                    dsi->flags |= DSI_RUNNING;
 
-                err = (*afp_switch[function])(obj,
-                                              (char *)&dsi->commands, dsi->cmdlen,
-                                              (char *)&dsi->data, &dsi->datalen);
+                    LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function));
+
+                    err = (*afp_switch[function])(obj,
+                                                  (char *)&dsi->commands, dsi->cmdlen,
+                                                  (char *)&dsi->data, &dsi->datalen);
+
+                    LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s",
+                        AfpNum2name(function), AfpErr2name(err));
+
+                    dir_free_invalid_q();
 
-                LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s",
-                    AfpNum2name(function), AfpErr2name(err));
 #ifdef FORCE_UIDGID
-               /* bring everything back to old euid, egid */
-                if (obj->force_uid)
-                   restore_uidgid ( &obj->uidgid );
+                    /* bring everything back to old euid, egid */
+                    if (obj->force_uid)
+                        restore_uidgid ( &obj->uidgid );
 #endif /* FORCE_UIDGID */
-                child.flags &= ~CHILD_RUNNING;
-            } else {
-                LOG(log_error, logtype_afpd, "bad function %X", function);
-                dsi->datalen = 0;
-                err = AFPERR_NOOP;
+                    dsi->flags &= ~DSI_RUNNING;
+
+                    /* Add result to the AFP replay cache */
+                    replaycache[rc_idx].DSIreqID = dsi->clientID;
+                    replaycache[rc_idx].AFPcommand = function;
+                    replaycache[rc_idx].result = err;
+                } else {
+                    LOG(log_error, logtype_afpd, "bad function %X", function);
+                    dsi->datalen = 0;
+                    err = AFPERR_NOOP;
+                }
             }
 
             /* single shot toggle that gets set by dsi_readinit. */
-            if (dsi->noreply) {
-                dsi->noreply = 0;
+            if (dsi->flags & DSI_NOREPLY) {
+                dsi->flags &= ~DSI_NOREPLY;
                 break;
             }
 
             if (!dsi_cmdreply(dsi, err)) {
                 LOG(log_error, logtype_afpd, "dsi_cmdreply(%d): %s", dsi->socket, strerror(errno) );
-                afp_dsi_die(EXITERR_CLNT);
+                dsi->flags |= DSI_DISCONNECTED;
             }
             break;
 
@@ -474,7 +633,7 @@ void afp_over_dsi(AFPObj *obj)
             function = (u_char) dsi->commands[0];
             if ( afp_switch[ function ] != NULL ) {
                 dsi->datalen = DSI_DATASIZ;
-                child.flags |= CHILD_RUNNING;
+                dsi->flags |= DSI_RUNNING;
 
                 LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function));
 
@@ -485,7 +644,7 @@ void afp_over_dsi(AFPObj *obj)
                 LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s",
                     AfpNum2name(function), AfpErr2name(err));
 
-                child.flags &= ~CHILD_RUNNING;
+                dsi->flags &= ~DSI_RUNNING;
 #ifdef FORCE_UIDGID
                /* bring everything back to old euid, egid */
                if (obj->force_uid)
@@ -499,7 +658,7 @@ void afp_over_dsi(AFPObj *obj)
 
             if (!dsi_wrtreply(dsi, err)) {
                 LOG(log_error, logtype_afpd, "dsi_wrtreply: %s", strerror(errno) );
-                afp_dsi_die(EXITERR_CLNT);
+                dsi->flags |= DSI_DISCONNECTED;
             }
             break;
 
index ff807404b36df7955379934ff0738544446bf3da..9bb48042fa1e98ffdb5033edf70173995c310740 100644 (file)
 
 #include <stdio.h>
 #include <stdlib.h>
-
-/* STDC check */
-#if STDC_HEADERS
 #include <string.h>
-#else /* STDC_HEADERS */
-#ifndef HAVE_STRCHR
-#define strchr index
-#define strrchr index
-#endif /* HAVE_STRCHR */
-char *strchr (), *strrchr ();
-#ifndef HAVE_MEMCPY
-#define memcpy(d,s,n) bcopy ((s), (d), (n))
-#define memmove(d,s,n) bcopy ((s), (d), (n))
-#endif /* ! HAVE_MEMCPY */
-#endif /* STDC_HEADERS */
-
 #include <ctype.h>
-#ifdef HAVE_UNISTD_H
 #include <unistd.h>
-#endif /* HAVE_UNISTD_H */
 #include <sys/param.h>
 #include <sys/socket.h>
 #include <atalk/logger.h>
 
 #include <netinet/in.h>
 #include <arpa/inet.h>
+
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #endif /* HAVE_NETDB_H */
 
+#ifdef ADMIN_GRP
+#include <grp.h>
+#include <sys/types.h>
+#endif /* ADMIN_GRP */
+
 #include <atalk/paths.h>
 #include <atalk/util.h>
+#include <atalk/compat.h>
+
 #include "globals.h"
 #include "status.h"
 #include "auth.h"
+#include "dircache.h"
 #include "fce_api.h"
 
-#include <atalk/compat.h>
-
-#ifdef ADMIN_GRP
-#include <grp.h>
-#include <sys/types.h>
-#endif /* ADMIN_GRP */
-
 #ifndef MIN
 #define MIN(a, b)  ((a) < (b) ? (a) : (b))
 #endif /* MIN */
@@ -153,6 +138,8 @@ void afp_options_free(struct afp_options *opt,
        free(opt->ntdomain);
     if (opt->ntseparator && (opt->ntseparator != save->ntseparator))
        free(opt->ntseparator);
+    if (opt->logconfig && (opt->logconfig != save->logconfig))
+       free(opt->logconfig);
 }
 
 /* initialize options */
@@ -165,6 +152,7 @@ void afp_options_init(struct afp_options *options)
     options->systemvol.name = _PATH_AFPDSYSVOL;
     options->configfile = _PATH_AFPDCONF;
     options->sigconffile = _PATH_AFPDSIGCONF;
+    options->uuidconf = _PATH_AFPDUUIDCONF;
     options->uampath = _PATH_AFPDUAMPATH;
     options->uamlist = "uams_dhx.so,uams_dhx2.so";
     options->guest = "nobody";
@@ -172,8 +160,9 @@ void afp_options_init(struct afp_options *options)
     options->transports = AFPTRANS_TCP; /*  TCP only */
     options->passwdfile = _PATH_AFPDPWFILE;
     options->tickleval = 30;
-    options->timeout = 4;
-    options->sleep = 10* 120; /* 10 h in 30 seconds tick */
+    options->timeout = 4;       /* 4 tickles = 2 minutes */
+    options->sleep = 10 * 60 * 2; /* 10 h in 30 seconds tick */
+    options->disconnected = 10 * 60 * 2; /* 10 h in 30 seconds tick */
     options->server_notif = 1;
     options->authprintdir = NULL;
     options->signatureopt = "auto";
@@ -195,6 +184,12 @@ void afp_options_init(struct afp_options *options)
     /* don't advertize slp by default */
     options->flags |= OPTION_NOSLP;
 #endif
+    options->dircachesize = DEFAULT_MAX_DIRCACHE_SIZE;
+    options->flags |= OPTION_ACL2MACCESS;
+    options->flags |= OPTION_UUID;
+    options->tcp_sndbuf = 0;    /* 0 means don't change OS default */
+    options->tcp_rcvbuf = 0;    /* 0 means don't change OS default */
+    options->dsireadbuf = 12;
 }
 
 /* parse an afpd.conf line. i'm doing it this way because it's
@@ -220,7 +215,10 @@ int afp_options_parseline(char *buf, struct afp_options *options)
     if (strstr(buf, " -slp"))
         options->flags &= ~OPTION_NOSLP;
 #endif
-
+#ifdef USE_ZEROCONF
+    if (strstr(buf, " -nozeroconf"))
+        options->flags |= OPTION_NOZEROCONF;
+#endif
     if (strstr(buf, " -nouservolfirst"))
         options->flags &= ~OPTION_USERVOLFIRST;
     if (strstr(buf, " -uservolfirst"))
@@ -237,6 +235,8 @@ int afp_options_parseline(char *buf, struct afp_options *options)
         options->flags |= OPTION_CUSTOMICON;
     if (strstr(buf, " -advertise_ssh"))
         options->flags |= OPTION_ANNOUNCESSH;
+    if (strstr(buf, " -noacl2maccess"))
+        options->flags &= ~OPTION_ACL2MACCESS;
 
     /* passwd bits */
     if (strstr(buf, " -nosavepassword"))
@@ -281,8 +281,22 @@ int afp_options_parseline(char *buf, struct afp_options *options)
         options->defaultvol.name = opt;
     if ((c = getoption(buf, "-systemvol")) && (opt = strdup(c)))
         options->systemvol.name = opt;
-    if ((c = getoption(buf, "-loginmesg")) && (opt = strdup(c)))
+    if ((c = getoption(buf, "-loginmesg")) && (opt = strdup(c))) {
+        int i = 0, j = 0;
+        while (c[i]) {
+            if (c[i] != '\\') {
+                opt[j++] = c[i];
+            } else {
+                i++;
+                if (c[i] == 'n')
+                    opt[j++] = '\n';
+            }
+            i++;
+        }
+        opt[j] = 0;
         options->loginmesg = opt;
+        
+    }
     if ((c = getoption(buf, "-guestname")) && (opt = strdup(c)))
         options->guest = opt;
     if ((c = getoption(buf, "-passwdfile")) && (opt = strdup(c)))
@@ -311,6 +325,12 @@ int afp_options_parseline(char *buf, struct afp_options *options)
         }
     }
 
+    if ((c = getoption(buf, "-dsireadbuf"))) {
+        options->dsireadbuf = atoi(c);
+        if (options->dsireadbuf < 6)
+            options->dsireadbuf = 6;
+    }
+
     if ((c = getoption(buf, "-server_quantum")))
         options->server_quantum = strtoul(c, NULL, 0);
 
@@ -332,6 +352,7 @@ int afp_options_parseline(char *buf, struct afp_options *options)
         char *optstr;
         if ((optstr = getoption(c, "-setuplog"))) {
             setuplog(optstr);
+            options->logconfig = optstr; /* at least store the last (possibly only) one */
             c += sizeof("-setuplog");
         }
     }
@@ -449,6 +470,15 @@ int afp_options_parseline(char *buf, struct afp_options *options)
     if ((c = getoption(buf, "-ntseparator")) && (opt = strdup(c)))
        options->ntseparator = opt;
 
+    if ((c = getoption(buf, "-dircachesize")))
+        options->dircachesize = atoi(c);
+     
+    if ((c = getoption(buf, "-tcpsndbuf")))
+        options->tcp_sndbuf = atoi(c);
+
+    if ((c = getoption(buf, "-tcprcvbuf")))
+        options->tcp_rcvbuf = atoi(c);
+
        if ((c = getoption(buf, "-fcelistener"))) {
                LOG(log_note, logtype_afpd, "Adding fce listener \"%s\"", c);
                fce_add_udp_socket(c);
@@ -458,7 +488,6 @@ int afp_options_parseline(char *buf, struct afp_options *options)
                fce_set_coalesce(c);
        }
 
-
     return 1;
 }
 
@@ -477,18 +506,15 @@ static void show_version( void )
 
        puts( "afpd has been compiled with support for these features:\n" );
 
-       printf( "        AFP3.x support:\t" );
-#ifdef AFP3x
-       puts( "Yes" );
-#else
-       puts( "No" );
-#endif
+       printf( "        AFP3.x support:\tYes\n" );
+        printf( "        TCP/IP Support:\t" );
+        puts( "Yes" );
 
-       printf( "      Transport layers:\t" );
+       printf( "DDP(AppleTalk) Support:\t" );
 #ifdef NO_DDP
-       puts( "TCP/IP" );
+       puts( "No" );
 #else
-       puts( "TCP/IP DDP" );
+       puts( "Yes" );
 #endif
 
        printf( "         CNID backends:\t" );
@@ -535,6 +561,13 @@ static void show_version_extended(void )
        puts( "No" );
 #endif
 
+       printf( "      Zeroconf support:\t" );
+#ifdef USE_ZEROCONF
+       puts( "Yes" );
+#else
+       puts( "No" );
+#endif
+
        printf( "  TCP wrappers support:\t" );
 #ifdef TCPWRAP
        puts( "Yes" );
@@ -583,6 +616,23 @@ static void show_version_extended(void )
 #else
        puts( "No" );
 #endif
+
+       printf( "           ACL support:\t" );
+#ifdef HAVE_ACLS
+       puts( "Yes" );
+#else
+       puts( "No" );
+#endif
+
+       printf( "            EA support:\t" );
+       puts( EA_MODULES );
+
+       printf( "          LDAP support:\t" );
+#ifdef HAVE_LDAP
+       puts( "Yes" );
+#else
+       puts( "No" );
+#endif
 }
 
 /*
@@ -591,11 +641,18 @@ static void show_version_extended(void )
 static void show_paths( void )
 {
        printf( "             afpd.conf:\t%s\n", _PATH_AFPDCONF );
-       printf( "    afp_signature.conf:\t%s\n", _PATH_AFPDSIGCONF );
        printf( "   AppleVolumes.system:\t%s\n", _PATH_AFPDSYSVOL );
        printf( "  AppleVolumes.default:\t%s\n", _PATH_AFPDDEFVOL );
+       printf( "    afp_signature.conf:\t%s\n", _PATH_AFPDSIGCONF );
+       printf( "      afp_voluuid.conf:\t%s\n", _PATH_AFPDUUIDCONF );
+#ifdef HAVE_LDAP
+       printf( "         afp_ldap.conf:\t%s\n", _PATH_ACL_LDAPCONF );
+#else
+       printf( "         afp_ldap.conf:\tnot supported\n");
+#endif
        printf( "       UAM search path:\t%s\n", _PATH_AFPDUAMPATH );
-    printf( "  Server messages path:\t%s\n", SERVERTEXT);
+       printf( "  Server messages path:\t%s\n", SERVERTEXT);
+       printf( "              lockfile:\t%s\n", _PATH_AFPDLOCK);
 }
 
 /*
diff --git a/etc/afpd/afp_zeroconf.c b/etc/afpd/afp_zeroconf.c
new file mode 100644 (file)
index 0000000..84f1871
--- /dev/null
@@ -0,0 +1,41 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
+/*
+ * Author:  Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose: Zeroconf facade, that abstracts access to a
+ *          particular Zeroconf implementation
+ * Doc:     http://www.dns-sd.org/
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "afp_zeroconf.h"
+#include "afp_config.h"
+
+#ifdef HAVE_AVAHI
+#include "afp_avahi.h"
+#endif
+
+
+/*
+ * Functions (actually they are just facades)
+ */
+void zeroconf_register(const AFPConfig *configs)
+{
+#if defined (HAVE_AVAHI)
+  LOG(log_debug, logtype_afpd, "Attempting to register with mDNS using Avahi");
+
+       av_zeroconf_setup(configs);
+  av_zeroconf_run();
+#endif
+}
+
+void zeroconf_deregister(void)
+{
+#if defined (HAVE_AVAHI)
+  LOG(log_debug, logtype_afpd, "Attempting to de-register mDNS using Avahi");
+       av_zeroconf_shutdown();
+#endif
+}
diff --git a/etc/afpd/afp_zeroconf.h b/etc/afpd/afp_zeroconf.h
new file mode 100644 (file)
index 0000000..e2711af
--- /dev/null
@@ -0,0 +1,29 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
+/*
+ * Author:  Daniel S. Haischt <me@daniel.stefan.haischt.name>
+ * Purpose: Zeroconf facade, that abstracts access to a
+ *          particular Zeroconf implementation
+ * Doc:     http://www.dns-sd.org/
+ *
+ */
+
+#ifndef AFPD_ZEROCONF_H
+#define AFPD_ZEROCONF_H
+
+#include "afp_config.h"
+
+/*
+ * Prototype Definitions
+ */
+
+/*
+ * registers service with a particular Zerconf implemenation.
+ */
+void zeroconf_register(const AFPConfig *configs);
+
+/*
+ * de-registers the ntpd service with a particular Zerconf implemenation.
+ */
+void zeroconf_deregister(void);
+
+#endif /* AFPD_ZEROCONF_H */
index 37850f62c471be718dd60f5046e3ce2325de32eb..fe9ff250336214d410a32bee946578865d490fd2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: appl.c,v 1.18 2009-10-15 10:43:13 didg Exp $
+ * $Id: appl.c,v 1.18.4.1 2010-02-01 10:56:08 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -20,6 +20,8 @@
 
 #include <atalk/adouble.h>
 #include <atalk/afp.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
 
 #include "volume.h"
 #include "globals.h"
@@ -122,7 +124,7 @@ static int copyapplfile(int sfd, int dfd, char *mpath, u_short mplen)
  * See afp_getappl() for the backward compatiblity code.
  */
 static char *
-makemacpath(char *mpath, int mpathlen, struct dir *dir, char *path)
+makemacpath(const struct vol *vol, char *mpath, int mpathlen, struct dir *dir, char *path)
 {
     char       *p;
 
@@ -130,16 +132,65 @@ makemacpath(char *mpath, int mpathlen, struct dir *dir, char *path)
     p -= strlen( path );
     memcpy( p, path, strlen( path )); 
 
-    while ( dir->d_parent != NULL ) {
-        p -= strlen( dir->d_m_name ) + 1;
+    while ( dir->d_did != DIRDID_ROOT ) {
+        p -= blength(dir->d_m_name) + 1;
         if (p < mpath) {
             /* FIXME: pathname too long */
             return NULL;
         }
-        strcpy( p, dir->d_m_name );
-        dir = dir->d_parent;
+        memcpy(p, cfrombstr(dir->d_m_name), blength(dir->d_m_name) + 1);
+        if ((dir = dirlookup(vol, dir->d_pdid)) == NULL)
+            return NULL;
     }
     return( p );
+
+
+#if 0
+    char buffer[12 + MAXPATHLEN + 1];
+    int buflen = 12 + MAXPATHLEN + 1;
+    char *ret = mpath;
+    char *path = name;
+    char *uname = NULL;
+    struct bstrList *pathlist = NULL;
+    cnid_t cnid = dir->d_pdid;
+
+    /* Create list for path elements, request 16 list elements for now*/
+    if ((pathlist = bstListCreateMin(16)) == NULL) {
+        LOG(log_error, logtype_afpd, "makemacpath: OOM: %s", strerror(errno));
+        return NULL;
+    }
+
+    while ( cnid != DIRDID_ROOT ) {
+
+        /* construct path, copy already found uname to path element list*/
+        if ((bstrListPush(pathlist, bfromcstr(path))) != BSTR_OK) {
+            afp_errno = AFPERR_MISC;
+            ret = NULL;
+            goto exit;
+        }
+
+        /* next part */
+        if ((uname = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL ) {
+            afp_errno = AFPERR_NOOBJ;
+            ret = NULL;
+            goto exit;
+        }
+
+        if ((path = utompath(vol, uname, cnid, utf8_encoding())) == NULL) {
+            afp_errno = AFPERR_MISC;
+            ret = NULL;
+            goto exit;
+        }
+    }
+
+
+
+exit:
+    if (pathlist)
+        bstrListDestroy(pathlist);
+
+    return(ret);
+#endif
 }
 
 
@@ -197,7 +248,7 @@ int afp_addappl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, siz
         return( AFPERR_PARAM );
     }
     mpath = obj->newtmp;
-    mp = makemacpath( mpath, AFPOBJ_TMPSIZ, curdir, path->m_name );
+    mp = makemacpath( vol, mpath, AFPOBJ_TMPSIZ, curdir, path->m_name );
     if (!mp) {
         return AFPERR_PARAM;
     }
@@ -280,7 +331,7 @@ int afp_rmvappl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, siz
         return( AFPERR_PARAM );
     }
     mpath = obj->newtmp;
-    mp = makemacpath( mpath, AFPOBJ_TMPSIZ, curdir, path->m_name );
+    mp = makemacpath( vol, mpath, AFPOBJ_TMPSIZ, curdir, path->m_name );
     if (!mp) {
         return AFPERR_PARAM ;
     }
@@ -418,7 +469,7 @@ int afp_getappl(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
     memcpy( q, p, len );
     q = cbuf;
 
-    if (( path = cname( vol, vol->v_dir, &q )) == NULL ) {
+    if (( path = cname( vol, vol->v_root, &q )) == NULL ) {
         *rbuflen = 0;
         return( AFPERR_NOITEM );
     }
index 429b353637e4d584d16b482d4c381d6e0b25741c..566a8d0e05b1b838ddc5e2dceef29463e9c33d03 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: auth.c,v 1.71 2009-11-30 15:30:47 franklahm Exp $
- *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
  */
@@ -28,9 +26,6 @@
 #include <time.h>
 #include <pwd.h>
 #include <grp.h>
-#include <atalk/logger.h>
-#include <atalk/server_ipc.h>
-#include <atalk/uuid.h>
 
 #ifdef TRU64
 #include <netdb.h>
 extern void afp_get_cmdline( int *ac, char ***av );
 #endif /* TRU64 */
 
+#include <atalk/logger.h>
+#include <atalk/server_ipc.h>
+#include <atalk/uuid.h>
+
 #include "globals.h"
 #include "auth.h"
 #include "uam_auth.h"
@@ -48,7 +47,7 @@ extern void afp_get_cmdline( int *ac, char ***av );
 #include "status.h"
 #include "fork.h"
 #include "extattrs.h"
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
 #include "acls.h"
 #endif
 
@@ -80,11 +79,10 @@ static struct afp_versions  afp_versions[] = {
     { "AFPVersion 2.1", 21 },
 #endif /* ! NO_DDP */
     { "AFP2.2", 22 },
-#ifdef AFP3x
     { "AFPX03", 30 },
     { "AFP3.1", 31 },
-    { "AFP3.2", 32 }
-#endif /* AFP3x */
+    { "AFP3.2", 32 },
+    { "AFP3.3", 33 }
 };
 
 static struct uam_mod uam_modules = {NULL, NULL, &uam_modules, &uam_modules};
@@ -209,21 +207,25 @@ static int set_auth_switch(int expired)
     else {
         afp_switch = postauth_switch;
         switch (afp_version) {
+
+        case 33:
         case 32:
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
             uam_afpserver_action(AFP_GETACL, UAM_AFPSERVER_POSTAUTH, afp_getacl, NULL);
             uam_afpserver_action(AFP_SETACL, UAM_AFPSERVER_POSTAUTH, afp_setacl, NULL);
             uam_afpserver_action(AFP_ACCESS, UAM_AFPSERVER_POSTAUTH, afp_access, NULL);
-#endif
+#endif /* HAVE_ACLS */
             uam_afpserver_action(AFP_GETEXTATTR, UAM_AFPSERVER_POSTAUTH, afp_getextattr, NULL);
             uam_afpserver_action(AFP_SETEXTATTR, UAM_AFPSERVER_POSTAUTH, afp_setextattr, NULL);
             uam_afpserver_action(AFP_REMOVEATTR, UAM_AFPSERVER_POSTAUTH, afp_remextattr, NULL);
             uam_afpserver_action(AFP_LISTEXTATTR, UAM_AFPSERVER_POSTAUTH, afp_listextattr, NULL);
+
         case 31:
             uam_afpserver_action(AFP_SYNCDIR, UAM_AFPSERVER_POSTAUTH, afp_syncdir, NULL);
             uam_afpserver_action(AFP_SYNCFORK, UAM_AFPSERVER_POSTAUTH, afp_syncfork, NULL);
             uam_afpserver_action(AFP_SPOTLIGHT_PRIVATE, UAM_AFPSERVER_POSTAUTH, afp_null_nolog, NULL);
             uam_afpserver_action(AFP_ENUMERATE_EXT2, UAM_AFPSERVER_POSTAUTH, afp_enumerate_ext2, NULL);
+
         case 30:
             uam_afpserver_action(AFP_ENUMERATE_EXT, UAM_AFPSERVER_POSTAUTH, afp_enumerate_ext, NULL);
             uam_afpserver_action(AFP_BYTELOCK_EXT,  UAM_AFPSERVER_POSTAUTH, afp_bytelock_ext, NULL);
@@ -263,8 +265,8 @@ static int login(AFPObj *obj, struct passwd *pwd, void (*logout)(void), int expi
         return AFPERR_NOTAUTH;
     }
 
-    LOG(log_info, logtype_afpd, "login %s (uid %d, gid %d) %s", pwd->pw_name,
-        pwd->pw_uid, pwd->pw_gid , afp_versions[afp_version_index].av_name);
+    LOG(log_note, logtype_afpd, "%s Login by %s",
+        afp_versions[afp_version_index].av_name, pwd->pw_name);
 
 #ifndef NO_DDP
     if (obj->proto == AFPPROTO_ASP) {
@@ -434,25 +436,57 @@ static int login(AFPObj *obj, struct passwd *pwd, void (*logout)(void), int expi
 }
 
 /* ---------------------- */
-int afp_zzz ( /* Function 122 */
-    AFPObj       *obj,
-    char         *ibuf _U_, size_t ibuflen _U_, 
-    char *rbuf, size_t *rbuflen)
+int afp_zzz(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
 {
-    u_int32_t   retdata;
+    uint32_t data;
+    DSI *dsi = (DSI *)AFPobj->handle;
 
     *rbuflen = 0;
+    ibuf += 2;
+    ibuflen -= 2;
+
+    if (ibuflen < 4)
+        return AFPERR_MISC;
+    memcpy(&data, ibuf, 4); /* flag */
+    data = ntohl(data);
+
+    /*
+     * Possible sleeping states:
+     * 1) normal sleep: DSI_SLEEPING (up to 10.3)
+     * 2) extended sleep: DSI_SLEEPING | DSI_EXTSLEEP (starting with 10.4)
+     */
+
+    if (data & AFPZZZ_EXT_WAKEUP) {
+        /* wakeup request from exetended sleep */
+        if (dsi->flags & DSI_EXTSLEEP) {
+            LOG(log_note, logtype_afpd, "afp_zzz: waking up from extended sleep");
+            dsi->flags &= ~(DSI_SLEEPING | DSI_EXTSLEEP);
+        }
+    } else {
+        /* sleep request */
+        dsi->flags |= DSI_SLEEPING;
+        if (data & AFPZZZ_EXT_SLEEP) {
+            LOG(log_note, logtype_afpd, "afp_zzz: entering extended sleep");
+            dsi->flags |= DSI_EXTSLEEP;
+        } else {
+            LOG(log_note, logtype_afpd, "afp_zzz: entering normal sleep");
+        }
+    }
 
-    retdata = obj->options.sleep /120;
-    if (!retdata) {
-        retdata = 1;
+    /*
+     * According to AFP 3.3 spec we should not return anything,
+     * but eg 10.5.8 server still returns the numbers of hours
+     * the server is keeping the sessino (ie max sleeptime).
+     */
+    data = obj->options.sleep / 120; /* hours */
+    if (!data) {
+        data = 1;
     }
-    *rbuflen = sizeof(retdata);
-    retdata = htonl(retdata);
-    memcpy(rbuf, &retdata, sizeof(retdata));
-    if (obj->sleep)
-        obj->sleep();
-    rbuf += sizeof(retdata);
+    *rbuflen = sizeof(data);
+    data = htonl(data);
+    memcpy(rbuf, &data, sizeof(data));
+    rbuf += sizeof(data);
+
     return AFP_OK;
 }
 
@@ -547,7 +581,7 @@ int afp_getsession(
             token = obj->sinfo.sessiontoken;
         }
         break;
-    case 3: /* Jaguar */
+    case 3:
     case 4:
         if (ibuflen >= 8 ) {
             p = ibuf;
@@ -560,7 +594,7 @@ int afp_getsession(
             if (ibuflen < idlen || idlen > (90-10)) {
                 return AFPERR_PARAM;
             }
-            server_ipc_write(IPC_GETSESSION, idlen+8, p );
+            ipc_child_write(obj->ipc_fd, IPC_GETSESSION, idlen+8, p);
             tklen = obj->sinfo.sessiontoken_len;
             token = obj->sinfo.sessiontoken;
         }
@@ -590,10 +624,10 @@ int afp_getsession(
 }
 
 /* ---------------------- */
-int afp_disconnect(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
+int afp_disconnect(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
+    DSI                 *dsi = (DSI *)obj->handle;
     u_int16_t           type;
-
     u_int32_t           tklen;
     pid_t               token;
     int                 i;
@@ -634,11 +668,41 @@ int afp_disconnect(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _
         }
     }
 
-    /* killed old session, not easy */
-    server_ipc_write(IPC_KILLTOKEN, tklen, &token);
-    sleep(1);
+    LOG(log_note, logtype_afpd, "afp_disconnect: trying primary reconnect");
+    dsi->flags |= DSI_RECONINPROG;
+
+    /* Deactivate tickle timer */
+    const struct itimerval none = {{0, 0}, {0, 0}};
+    setitimer(ITIMER_REAL, &none, NULL);
 
-    return AFPERR_SESSCLOS;   /* was AFP_OK */
+    /* check for old session, possibly transfering session from here to there */
+    if (ipc_child_write(obj->ipc_fd, IPC_DISCOLDSESSION, tklen, &token) == -1)
+        goto exit;
+    /* write uint16_t DSI request ID */
+    if (writet(obj->ipc_fd, &dsi->header.dsi_requestID, 2, 0, 2) != 2) {
+        LOG(log_error, logtype_afpd, "afp_disconnect: couldn't send DSI request ID");
+        goto exit;
+    }
+    /* now send our connected AFP client socket */
+    if (send_fd(obj->ipc_fd, dsi->socket) != 0)
+        goto exit;
+    /* Now see what happens: either afpd master sends us SIGTERM because our session */
+    /* has been transfered to a old disconnected session, or we continue    */
+    sleep(5);
+
+    if (!(dsi->flags & DSI_RECONINPROG)) { /* deleted in SIGTERM handler */
+        /* Reconnect succeeded, we exit now after sleeping some more */
+        sleep(2); /* sleep some more to give the recon. session time */
+        LOG(log_note, logtype_afpd, "afp_disconnect: primary reconnect succeeded");
+        exit(0);
+    }
+
+exit:
+    /* Reinstall tickle timer */
+    setitimer(ITIMER_REAL, &dsi->timer, NULL);
+
+    LOG(log_error, logtype_afpd, "afp_disconnect: primary reconnect failed");
+    return AFPERR_MISC;
 }
 
 /* ---------------------- */
@@ -871,11 +935,15 @@ int afp_logincont(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *r
 }
 
 
-int afp_logout(AFPObj *obj, char *ibuf _U_, size_t ibuflen  _U_, char *rbuf  _U_, size_t *rbuflen  _U_)
+int afp_logout(AFPObj *obj, char *ibuf _U_, size_t ibuflen  _U_, char *rbuf  _U_, size_t *rbuflen)
 {
-    LOG(log_info, logtype_afpd, "logout %s", obj->username);
+    DSI *dsi = (DSI *)(obj->handle);
+
+    LOG(log_note, logtype_afpd, "AFP logout by %s", obj->username);
+    of_close_all_forks();
     close_all_vol();
-    obj->exit(0);
+    dsi->flags = DSI_AFP_LOGGED_OUT;
+    *rbuflen = 0;
     return AFP_OK;
 }
 
@@ -961,6 +1029,7 @@ int afp_getuserinfo(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf,
     u_int8_t  thisuser;
     u_int32_t id;
     u_int16_t bitmap;
+    char *bitmapp;
 
     LOG(log_debug, logtype_afpd, "begin afp_getuserinfo:");
 
@@ -979,8 +1048,9 @@ int afp_getuserinfo(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf,
     if ((bitmap & USERIBIT_ALL) != bitmap)
         return AFPERR_BITMAP;
 
-    /* copy the bitmap back to reply buffer */
+    /* remember place where we store the possibly modified bitmap later */
     memcpy(rbuf, ibuf, sizeof(bitmap));
+    bitmapp = rbuf;
     rbuf += sizeof(bitmap);
     *rbuflen = sizeof(bitmap);
 
@@ -999,29 +1069,27 @@ int afp_getuserinfo(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf,
         *rbuflen += sizeof(id);
     }
 
-#ifdef HAVE_NFSv4_ACLS
     if (bitmap & USERIBIT_UUID) {
-        int ret;
-        uuid_t uuid;
-        char *uuidstring;
-
-        if ( ! (obj->options.flags & OPTION_UUID))
-            return AFPERR_BITMAP;
-        LOG(log_debug, logtype_afpd, "afp_getuserinfo: get UUID for \'%s\'", obj->username);
-        ret = getuuidfromname( obj->username, UUID_USER, uuid);
-        if (ret != 0) {
-            LOG(log_info, logtype_afpd, "afp_getuserinfo: error getting UUID !");
-            return AFPERR_NOITEM;
-        }
-        if (0 == (uuid_bin2string( uuid, &uuidstring))) {
-            LOG(log_debug, logtype_afpd, "afp_getuserinfo: got UUID: %s", uuidstring);
-            free(uuidstring);
+        if ( ! (obj->options.flags & OPTION_UUID)) {
+            bitmap &= ~USERIBIT_UUID;
+            bitmap = htons(bitmap);
+            memcpy(bitmapp, &bitmap, sizeof(bitmap));
+        } else {
+            LOG(log_debug, logtype_afpd, "afp_getuserinfo: get UUID for \'%s\'", obj->username);
+            int ret;
+            atalk_uuid_t uuid;
+            ret = getuuidfromname( obj->username, UUID_USER, uuid);
+            if (ret != 0) {
+                LOG(log_info, logtype_afpd, "afp_getuserinfo: error getting UUID !");
+                return AFPERR_NOITEM;
+            }
+            LOG(log_debug, logtype_afpd, "afp_getuserinfo: got UUID: %s", uuid_bin2string(uuid));
+
+            memcpy(rbuf, uuid, UUID_BINSIZE);
+            rbuf += UUID_BINSIZE;
+            *rbuflen += UUID_BINSIZE;
         }
-        memcpy(rbuf, uuid, UUID_BINSIZE);
-        rbuf += UUID_BINSIZE;
-        *rbuflen += UUID_BINSIZE;
     }
-#endif
 
     LOG(log_debug, logtype_afpd, "END afp_getuserinfo:");
     return AFP_OK;
@@ -1093,9 +1161,9 @@ int auth_load(const char *path, const char *list)
         if (stat(name, &st) == 0) {
             if ((mod = uam_load(name, p))) {
                 uam_attach(&uam_modules, mod);
-                LOG(log_info, logtype_afpd, "uam: %s loaded", p);
+                LOG(log_debug, logtype_afpd, "uam: %s loaded", p);
             } else {
-                LOG(log_info, logtype_afpd, "uam: %s load failure",p);
+                LOG(log_error, logtype_afpd, "uam: %s load failure",p);
             }
         } else {
             LOG(log_info, logtype_afpd, "uam: uam not found (status=%d)", stat(name, &st));
index a1e96dfc50ab74649f67c09569f6ea8c99818e37..6a9161e2e8720743ca3cbbf4100b0980de9d34b0 100644 (file)
@@ -1,6 +1,7 @@
 /* 
  * Netatalk 2002 (c)
  * Copyright (C) 1990, 1993 Regents of The University of Michigan
+ * Copyright (C) 2010 Frank Lahm
  * All Rights Reserved. See COPYRIGHT
  */
 
  *
  * Initial version written by Rafal Lewczuk <rlewczuk@pronet.pl>
  *
+ * Starting with Netatalk 2.2 searching by name criteria utilizes the
+ * CNID database in conjunction with an enhanced cnid_dbd. This requires
+ * the use of cnidscheme:dbd for the searched volume, the new functionality
+ * is not built into cnidscheme:cdb.
  */
 
 #ifdef HAVE_CONFIG_H
 #include <ctype.h>
 #include <string.h>
 #include <time.h>
-
-#if STDC_HEADERS
 #include <string.h>
-#else
-#ifndef HAVE_MEMCPY
-#define memcpy(d,s,n) bcopy ((s), (d), (n))
-#define memmove(d,s,n) bcopy ((s), (d), (n))
-#endif /* ! HAVE_MEMCPY */
-#endif
-
 #include <sys/file.h>
 #include <netinet/in.h>
 
 #include <atalk/afp.h>
 #include <atalk/adouble.h>
 #include <atalk/logger.h>
-#ifdef CNID_DB
 #include <atalk/cnid.h>
-#endif /* CNID_DB */
+#include <atalk/cnid_dbd_private.h>
 #include <atalk/util.h>
+#include <atalk/bstradd.h>
+#include <atalk/unicode.h>
 
 #include "desktop.h"
 #include "directory.h"
+#include "dircache.h"
 #include "file.h"
 #include "volume.h"
 #include "globals.h"
@@ -110,7 +108,8 @@ struct scrit {
  *
  */
 struct dsitem {
-       struct dir *dir; /* Structure describing this directory */
+//     struct dir *dir; /* Structure describing this directory */
+//  cnid_t did;      /* CNID of this directory           */
        int pidx;        /* Parent's dsitem structure index. */
        int checked;     /* Have we checked this directory ? */
        int path_len;
@@ -143,7 +142,7 @@ static int addstack(char *uname, struct dir *dir, int pidx)
 
        /* Put new element. Allocate and copy lname and path. */
        ds = dstack + dsidx++;
-       ds->dir = dir;
+//     ds->did = dir->d_did;
        ds->pidx = pidx;
        ds->checked = 0;
        if (pidx >= 0) {
@@ -479,21 +478,33 @@ static int rslt_add ( struct vol *vol, struct path *path, char **buf, int ext)
 #define VETO_STR \
         "./../.AppleDouble/.AppleDB/Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/.AppleDesktop/.Parent/"
 
-/* This function performs search. It is called directly from afp_catsearch 
- * vol - volume we are searching on ...
- * dir - directory we are starting from ...
- * c1, c2 - search criteria
- * rmatches - maximum number of matches we can return
- * pos - position we've stopped recently
- * rbuf - output buffer
- * rbuflen - output buffer length
+/*!
+ * This function performs a filesystem search
+ *
+ * Uses globals c1, c2, the search criteria
+ *
+ * @param vol       (r)  volume we are searching on ...
+ * @param dir       (rw) directory we are starting from ...
+ * @param rmatches  (r)  maximum number of matches we can return
+ * @param pos       (r)  position we've stopped recently
+ * @param rbuf      (w)  output buffer
+ * @param nrecs     (w)  number of matches
+ * @param rsize     (w)  length of data written to output buffer
+ * @param ext       (r)  extended search flag
  */
 #define NUM_ROUNDS 200
-static int catsearch(struct vol *vol, struct dir *dir,  
-                    int rmatches, u_int32_t *pos, char *rbuf, u_int32_t *nrecs, int *rsize, int ext)
+static int catsearch(struct vol *vol,
+                     struct dir *dir,  
+                     int rmatches,
+                     uint32_t *pos,
+                     char *rbuf,
+                     uint32_t *nrecs,
+                     int *rsize,
+                     int ext)
 {
     static u_int32_t cur_pos;    /* Saved position index (ID) - used to remember "position" across FPCatSearch calls */
     static DIR *dirpos;                 /* UNIX structure describing currently opened directory. */
+    struct dir *curdir;          /* struct dir of current directory */
        int cidx, r;
        struct dirent *entry;
        int result = AFP_OK;
@@ -503,7 +514,6 @@ static int catsearch(struct vol *vol, struct dir *dir,
        char *rrbuf = rbuf;
     time_t start_time;
     int num_rounds = NUM_ROUNDS;
-    int cached;
     int cwd = -1;
     int error;
         
@@ -540,14 +550,14 @@ static int catsearch(struct vol *vol, struct dir *dir,
     start_time = time(NULL);
 
        while ((cidx = reducestack()) != -1) {
-               cached = 1;
-
                error = lchdir(dstack[cidx].path);
 
-               if (!error && dirpos == NULL) {
+               if (!error && dirpos == NULL)
                        dirpos = opendir(".");
-                       cached = (dstack[cidx].dir->d_child != NULL);
-               }
+
+               if (dirpos == NULL)
+                       dirpos = opendir(dstack[cidx].path);
+
                if (error || dirpos == NULL) {
                        switch (errno) {
                        case EACCES:
@@ -565,8 +575,13 @@ static int catsearch(struct vol *vol, struct dir *dir,
                        } /* switch (errno) */
                        goto catsearch_end;
                }
+
+        if ((curdir = dirlookup_bypath(vol, dstack[cidx].path)) == NULL) {
+            result = AFPERR_MISC;
+            goto catsearch_end;
+        }
                
-               while ((entry=readdir(dirpos)) != NULL) {
+               while ((entry = readdir(dirpos)) != NULL) {
                        (*pos)++;
 
                        if (!check_dirent(vol, entry->d_name))
@@ -594,28 +609,32 @@ static int catsearch(struct vol *vol, struct dir *dir,
                                   ie if in the same loop the parent dir wasn't in the cache
                                   ALL dirsearch_byname will fail.
                                */
-                               if (cached)
-                       path.d_dir = dirsearch_byname(vol, dstack[cidx].dir, path.u_name);
-               else
-                       path.d_dir = NULL;
-               if (!path.d_dir) {
+                int unlen = strlen(path.u_name);
+                path.d_dir = dircache_search_by_name(vol,
+                                                     curdir,
+                                                     path.u_name,
+                                                     unlen,
+                                                     path.st.st_ctime);
+               if (path.d_dir == NULL) {
                        /* path.m_name is set by adddir */
-                   if (NULL == (path.d_dir = adddir( vol, dstack[cidx].dir, &path) ) ) {
+                   if ((path.d_dir = dir_add(vol,
+                                              curdir,
+                                              &path,
+                                              unlen)) == NULL) {
                                                result = AFPERR_MISC;
                                                goto catsearch_end;
                                        }
                 }
-                path.m_name = path.d_dir->d_m_name; 
+                path.m_name = cfrombstr(path.d_dir->d_m_name);
                        
                                if (addstack(path.u_name, path.d_dir, cidx) == -1) {
                                        result = AFPERR_MISC;
                                        goto catsearch_end;
                                } 
+            } else {
+               path.d_dir = curdir;
             }
-            else {
-               /* yes it sucks for directory d_dir is the directory, for file it's the parent directory*/
-               path.d_dir = dstack[cidx].dir;
-            }
+
                        ccr = crit_check(vol, &path);
 
                        /* bit 0 means that criteria has been met */
@@ -667,6 +686,156 @@ catsearch_end: /* Exiting catsearch: error condition */
        return result;
 } /* catsearch() */
 
+/*!
+ * This function performs a CNID db search
+ *
+ * Uses globals c1, c2, the search criteria
+ *
+ * @param vol       (r)  volume we are searching on ...
+ * @param dir       (rw) directory we are starting from ...
+ * @param uname     (r)  UNIX name of object to search
+ * @param rmatches  (r)  maximum number of matches we can return
+ * @param pos       (r)  position we've stopped recently
+ * @param rbuf      (w)  output buffer
+ * @param nrecs     (w)  number of matches
+ * @param rsize     (w)  length of data written to output buffer
+ * @param ext       (r)  extended search flag
+ */
+static int catsearch_db(struct vol *vol,
+                        struct dir *dir,  
+                        const char *uname,
+                        int rmatches,
+                        uint32_t *pos,
+                        char *rbuf,
+                        uint32_t *nrecs,
+                        int *rsize,
+                        int ext)
+{
+    static char resbuf[DBD_MAX_SRCH_RSLTS * sizeof(cnid_t)];
+    static uint32_t cur_pos;
+    static int num_matches;
+    int ccr ,r;
+       int result = AFP_OK;
+    struct path path;
+       char *rrbuf = rbuf;
+    char buffer[MAXPATHLEN +2];
+    uint16_t flags = CONV_TOLOWER;
+
+    LOG(log_debug, logtype_afpd, "catsearch_db(req pos: %u): {pos: %u, name: %s}",
+        *pos, cur_pos, uname);
+        
+       if (*pos != 0 && *pos != cur_pos) {
+               result = AFPERR_CATCHNG;
+               goto catsearch_end;
+       }
+
+    if (cur_pos == 0 || *pos == 0) {
+        if (convert_charset(vol->v_volcharset,
+                            vol->v_volcharset,
+                            vol->v_maccharset,
+                            uname,
+                            strlen(uname),
+                            buffer,
+                            MAXPATHLEN,
+                            &flags) == (size_t)-1) {
+            LOG(log_error, logtype_afpd, "catsearch_db: conversion error");
+            result = AFPERR_MISC;
+            goto catsearch_end;
+        }
+
+        LOG(log_debug, logtype_afpd, "catsearch_db: %s", buffer);
+
+        if ((num_matches = cnid_find(vol->v_cdb,
+                                     buffer,
+                                     strlen(uname),
+                                     resbuf,
+                                     sizeof(resbuf))) == -1) {
+            result = AFPERR_MISC;
+            goto catsearch_end;
+        }
+    }
+       
+       while (cur_pos < num_matches) {
+        char *name;
+        cnid_t cnid, did;
+        char resolvebuf[12 + MAXPATHLEN + 1];
+        struct dir *dir;
+
+        /* Next CNID to process from buffer */
+        memcpy(&cnid, resbuf + cur_pos * sizeof(cnid_t), sizeof(cnid_t));
+        did = cnid;
+
+        if ((name = cnid_resolve(vol->v_cdb, &did, resolvebuf, 12 + MAXPATHLEN + 1)) == NULL)
+            goto next;
+        LOG(log_debug, logtype_afpd, "catsearch_db: {pos: %u, name:%s, cnid: %u}",
+            cur_pos, name, ntohl(cnid));
+        if ((dir = dirlookup(vol, did)) == NULL)
+            goto next;
+        if (movecwd(vol, dir) < 0 )
+            goto next;
+
+        memset(&path, 0, sizeof(path));
+        path.u_name = name;
+        path.m_name = utompath(vol, name, cnid, utf8_encoding());
+
+        if (of_stat(&path) != 0) {
+            switch (errno) {
+            case EACCES:
+            case ELOOP:
+                goto next;
+            case ENOENT:
+                
+            default:
+                result = AFPERR_MISC;
+                goto catsearch_end;
+            } 
+        }
+        /* For files path.d_dir is the parent dir, for dirs its the dir itself */
+        if (S_ISDIR(path.st.st_mode))
+            if ((dir = dirlookup(vol, cnid)) == NULL)
+                goto next;
+        path.d_dir = dir;
+
+        LOG(log_maxdebug, logtype_afpd,"catsearch_db: dir: %s, cwd: %s, name: %s", 
+            cfrombstr(dir->d_fullpath), getcwdpath(), path.u_name);
+
+        /* At last we can check the search criteria */
+        ccr = crit_check(vol, &path);
+        if ((ccr & 1)) {
+            LOG(log_debug, logtype_afpd,"catsearch_db: match: %s/%s",
+                getcwdpath(), path.u_name);
+            /* bit 1 means that criteria has been met */
+            r = rslt_add(vol, &path, &rrbuf, ext);
+            if (r == 0) {
+                result = AFPERR_MISC;
+                goto catsearch_end;
+            } 
+            *nrecs += r;
+            /* Number of matches limit */
+            if (--rmatches == 0) 
+                goto catsearch_pause;
+            /* Block size limit */
+            if (rrbuf - rbuf >= 448)
+                goto catsearch_pause;
+        }
+    next:
+        cur_pos++;
+    } /* while */
+
+       /* finished */
+       result = AFPERR_EOF;
+    cur_pos = 0;
+       goto catsearch_end;
+
+catsearch_pause:
+    *pos = cur_pos;
+
+catsearch_end: /* Exiting catsearch: error condition */
+       *rsize = rrbuf - rbuf;
+    LOG(log_debug, logtype_afpd, "catsearch_db(req pos: %u): {pos: %u}", *pos, cur_pos);
+       return result;
+}
+
 /* -------------------------- */
 static int catsearch_afp(AFPObj *obj _U_, char *ibuf, size_t ibuflen,
                   char *rbuf, size_t *rbuflen, int ext)
@@ -683,7 +852,8 @@ static int catsearch_afp(AFPObj *obj _U_, char *ibuf, size_t ibuflen,
     size_t     len;
     u_int16_t  namelen;
     u_int16_t  flags;
-    char       tmppath[256];
+    char           tmppath[256];
+    char        *uname;
 
     *rbuflen = 0;
 
@@ -854,11 +1024,13 @@ static int catsearch_afp(AFPObj *obj _U_, char *ibuf, size_t ibuflen,
                /* length */
                memcpy(&namelen, spec1, sizeof(namelen));
                namelen = ntohs (namelen);
-               if (namelen > 255)  /* Safeguard */
-                       namelen = 255;
+               if (namelen > UTF8FILELEN_EARLY)  /* Safeguard */
+                       namelen = UTF8FILELEN_EARLY;
 
                memcpy (c1.utf8name, spec1+2, namelen);
-               c1.utf8name[(namelen+1)] =0;
+               c1.utf8name[namelen] = 0;
+        if ((uname = mtoupath(vol, c1.utf8name, 0, utf8_encoding())) == NULL)
+            return AFPERR_PARAM;
 
                /* convert charset */
                flags = CONV_PRECOMPOSE;
@@ -869,7 +1041,15 @@ static int catsearch_afp(AFPObj *obj _U_, char *ibuf, size_t ibuflen,
     
     /* Call search */
     *rbuflen = 24;
-    ret = catsearch(vol, vol->v_dir, rmatches, &catpos[0], rbuf+24, &nrecs, &rsize, ext);
+    if ((c1.rbitmap & (1 << FILPBIT_PDINFO))
+        && (strcmp(vol->v_cnidscheme, "dbd") == 0)
+        && (vol->v_flags & AFPVOL_SEARCHDB))
+        /* we've got a name and it's a dbd volume, so search CNID database */
+        ret = catsearch_db(vol, vol->v_root, uname, rmatches, &catpos[0], rbuf+24, &nrecs, &rsize, ext);
+    else
+        /* perform a slow filesystem tree search */
+        ret = catsearch(vol, vol->v_root, rmatches, &catpos[0], rbuf+24, &nrecs, &rsize, ext);
+
     memcpy(rbuf, catpos, sizeof(catpos));
     rbuf += sizeof(catpos);
 
index 5db36f71925a0cf0dd06f04fab94bca8f14acae0..9f105e13e13c612757386f70c342626e28fe5e77 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: desktop.c,v 1.50 2010-01-22 04:40:38 didg Exp $
+ * $Id: desktop.c,v 1.50.2.1 2010-02-01 10:56:08 franklahm Exp $
  *
  * See COPYRIGHT.
  *
@@ -600,7 +600,8 @@ char *mtoupath(const struct vol *vol, char *mpath, cnid_t did, int utf8)
     u_int16_t   flags;
         
     if ( *mpath == '\0' ) {
-        return( "." );
+        strcpy(upath, ".");
+        return upath;
     }
 
     /* set conversion flags */
@@ -700,7 +701,7 @@ static int ad_addcomment(struct vol *vol, struct path *path, char *ibuf)
     if (ad_getentryoff(adp, ADEID_COMMENT)) {
         if ( (ad_get_MD_flags( adp ) & O_CREAT) ) {
             if ( *path->m_name == '\0' ) {
-                name = curdir->d_m_name;
+                name = (char *)curdir->d_m_name->data;
             } else {
                 name = path->m_name;
             }
diff --git a/etc/afpd/dircache.c b/etc/afpd/dircache.c
new file mode 100644 (file)
index 0000000..9907c5d
--- /dev/null
@@ -0,0 +1,687 @@
+/*
+  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+  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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <time.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+#include <atalk/queue.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
+
+#include "dircache.h"
+#include "directory.h"
+#include "hash.h"
+#include "globals.h"
+
+/*
+ * Directory Cache
+ * ===============
+ *
+ * Cache files and directories in a LRU cache.
+ *
+ * The directory cache caches directories and files(!). The main reason for having the cache
+ * is avoiding recursive walks up the path, querying the CNID database each time, when
+ * we have to calculate the location of eg directory with CNID 30, which is located in a dir with
+ * CNID 25, next CNID 20 and then CNID 2 (the volume root as per AFP spec).
+ * If all these dirs where in the cache, each database look up can be avoided. Additionally there's
+ * the element "fullpath" in struct dir, which is used to avoid the recursion in any case. Wheneveer
+ * a struct dir is initialized, the fullpath to the directory is stored there.
+ *
+ * In order to speed up the CNID query for files too, which eg happens when a directory is enumerated,
+ * files are stored too in the dircache. In order to differentiate between files and dirs, we re-use
+ * the element fullpath, which for files is always NULL.
+ *
+ * The most frequent codepatch that leads to caching is directory enumeration (cf enumerate.c):
+ * - if a element is a directory:
+ *   (1) the cache is searched by dircache_search_by_name()
+ *   (2) if it wasn't found a new struct dir is created and cached both from within dir_add()
+ * - for files the caching happens a little bit down the call chain:
+ *   (3) first getfilparams() is called, which calls
+ *   (4) getmetadata() where the cache is searched with dircache_search_by_name()
+ *   (5) if the element is not found
+ *   (6) get_id() queries the CNID from the database
+ *   (7) then a struct dir is initialized via dir_new() (note the fullpath arg is NULL)
+ *   (8) finally added to the cache with dircache_add()
+ * (2) of course does contain the steps 6,7 and 8.
+ *
+ * The dircache is a LRU cache, whenever it fills up we call dircache_evict internally which removes
+ * DIRCACHE_FREE_QUANTUM elements from the cache.
+ *
+ * There is only one cache for all volumes, so of course we use the volume id in hashing calculations.
+ *
+ * In order to avoid cache poisoning, we store the cached entries st_ctime from stat in
+ * struct dir.ctime_dircache. Later when we search the cache we compare the stored
+ * value with the result of a fresh stat. If the times differ, we remove the cached
+ * entry and return "no entry found in cache".
+ * A elements ctime changes when
+ *   1) the element is renamed
+ *      (we loose the cached entry here, but it will expire when the cache fills)
+ *   2) its a directory and an object has been created therein
+ *   3) the element is deleted and recreated under the same name
+ * Using ctime leads to cache eviction in case 2) where it wouldn't be necessary, because
+ * the dir itself (name, CNID, ...) hasn't changed, but there's no other way.
+ *
+ * Indexes
+ * =======
+ *
+ * The maximum dircache size is:
+ * max(DEFAULT_MAX_DIRCACHE_SIZE, min(size, MAX_POSSIBLE_DIRCACHE_SIZE)).
+ * It is a hashtable which we use to store "struct dir"s in. If the cache get full, oldest
+ * entries are evicted in chunks of DIRCACHE_FREE.
+ *
+ * We have/need two indexes:
+ * - a DID/name index on the main dircache, another hashtable
+ * - a queue index on the dircache, for evicting the oldest entries
+ *
+ * Debugging
+ * =========
+ *
+ * Sending SIGHUP to a afpd child causes it to dump the dircache to a file "/tmp/dircache.PID".
+ */
+
+/********************************************************
+ * Local funcs and variables
+ ********************************************************/
+
+/*****************************
+ *       the dircache        */
+
+static hash_t       *dircache;        /* The actual cache */
+static unsigned int dircache_maxsize; /* cache maximum size */
+
+static struct dircache_stat {
+    unsigned long long lookups;
+    unsigned long long hits;
+    unsigned long long misses;
+    unsigned long long added;
+    unsigned long long removed;
+    unsigned long long expunged;
+    unsigned long long evicted;
+} dircache_stat;
+
+/* FNV 1a */
+static hash_val_t hash_vid_did(const void *key)
+{
+    const struct dir *k = (const struct dir *)key;
+    hash_val_t hash = 2166136261;
+
+    hash ^= k->d_vid >> 8;
+    hash *= 16777619;
+    hash ^= k->d_vid;
+    hash *= 16777619;
+
+    hash ^= k->d_did >> 24;
+    hash *= 16777619;
+    hash ^= (k->d_did >> 16) & 0xff;
+    hash *= 16777619;
+    hash ^= (k->d_did >> 8) & 0xff;
+    hash *= 16777619;
+    hash ^= (k->d_did >> 0) & 0xff;
+    hash *= 16777619;
+
+    return hash;
+}
+
+static int hash_comp_vid_did(const void *key1, const void *key2)
+{
+    const struct dir *k1 = key1;
+    const struct dir *k2 = key2;
+
+    return !(k1->d_did == k2->d_did && k1->d_vid == k2->d_vid);
+}
+
+/**************************************************
+ * DID/name index on dircache (another hashtable) */
+
+static hash_t *index_didname;
+
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__)    \
+    || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)    \
+                      +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+
+static hash_val_t hash_didname(const void *p)
+{
+    const struct dir *key = (const struct dir *)p;
+    const unsigned char *data = key->d_u_name->data;
+    int len = key->d_u_name->slen;
+    hash_val_t hash = key->d_pdid + key->d_vid;
+    hash_val_t tmp;
+
+    int rem = len & 3;
+    len >>= 2;
+
+    /* Main loop */
+    for (;len > 0; len--) {
+        hash  += get16bits (data);
+        tmp    = (get16bits (data+2) << 11) ^ hash;
+        hash   = (hash << 16) ^ tmp;
+        data  += 2*sizeof (uint16_t);
+        hash  += hash >> 11;
+    }
+
+    /* Handle end cases */
+    switch (rem) {
+    case 3: hash += get16bits (data);
+        hash ^= hash << 16;
+        hash ^= data[sizeof (uint16_t)] << 18;
+        hash += hash >> 11;
+        break;
+    case 2: hash += get16bits (data);
+        hash ^= hash << 11;
+        hash += hash >> 17;
+        break;
+    case 1: hash += *data;
+        hash ^= hash << 10;
+        hash += hash >> 1;
+    }
+
+    /* Force "avalanching" of final 127 bits */
+    hash ^= hash << 3;
+    hash += hash >> 5;
+    hash ^= hash << 4;
+    hash += hash >> 17;
+    hash ^= hash << 25;
+    hash += hash >> 6;
+
+    return hash;
+}
+
+static int hash_comp_didname(const void *k1, const void *k2)
+{
+    const struct dir *key1 = (const struct dir *)k1;
+    const struct dir *key2 = (const struct dir *)k2;
+
+    return ! (key1->d_vid == key2->d_vid
+              && key1->d_pdid == key2->d_pdid
+              && (bstrcmp(key1->d_u_name, key2->d_u_name) == 0) );
+}
+
+/***************************
+ * queue index on dircache */
+
+static q_t *index_queue;    /* the index itself */
+static unsigned long queue_count;
+
+/*!
+ * @brief Remove a fixed number of (oldest) entries from the cache and indexes
+ *
+ * The default is to remove the 256 oldest entries from the cache.
+ * 1. Get the oldest entry
+ * 2. If it's in use ie open forks reference it or it's curdir requeue it,
+ *    dont remove it
+ * 3. Remove the dir from the main cache and the didname index
+ * 4. Free the struct dir structure and all its members
+ */
+static void dircache_evict(void)
+{
+    int i = DIRCACHE_FREE_QUANTUM;
+    struct dir *dir;
+
+    LOG(log_debug, logtype_afpd, "dircache: {starting cache eviction}");
+
+    while (i--) {
+        if ((dir = (struct dir *)dequeue(index_queue)) == NULL) { /* 1 */
+            dircache_dump();
+            AFP_PANIC("dircache_evict");
+        }
+        queue_count--;
+
+        if (curdir == dir) {                          /* 2 */
+            if ((dir->qidx_node = enqueue(index_queue, dir)) == NULL) {
+                dircache_dump();
+                AFP_PANIC("dircache_evict");
+            }
+            queue_count++;
+            continue;
+        }
+
+        dircache_remove(NULL, dir, DIRCACHE | DIDNAME_INDEX); /* 3 */
+        dir_free(dir);                                        /* 4 */
+    }
+
+    AFP_ASSERT(queue_count == dircache->hash_nodecount);
+    dircache_stat.evicted += DIRCACHE_FREE_QUANTUM;
+    LOG(log_debug, logtype_afpd, "dircache: {finished cache eviction}");
+}
+
+
+/********************************************************
+ * Interface
+ ********************************************************/
+
+/*!
+ * @brief Search the dircache via a CNID for a directory
+ *
+ * Found cache entries are expunged if both the parent directory st_ctime and the objects
+ * st_ctime are modified.
+ * This func builds on the fact, that all our code only ever needs to and does search
+ * the dircache by CNID expecting directories to be returned, but not files.
+ * Thus
+ * (1) if we find a file (d_fullpath == NULL) for a given CNID we
+ *     (1a) remove it from the cache
+ *     (1b) return NULL indicating nothing found
+ * (2) we can then use d_fullpath to stat the directory
+ *
+ * @param vol      (r) pointer to struct vol
+ * @param cnid     (r) CNID of the directory to search
+ *
+ * @returns            Pointer to struct dir if found, else NULL
+ */
+struct dir *dircache_search_by_did(const struct vol *vol, cnid_t cnid)
+{
+    struct dir *cdir = NULL;
+    struct dir key;
+    struct stat st;
+    hnode_t *hn;
+
+    AFP_ASSERT(vol);
+    AFP_ASSERT(ntohl(cnid) >= CNID_START);
+
+    dircache_stat.lookups++;
+    key.d_vid = vol->v_vid;
+    key.d_did = cnid;
+    if ((hn = hash_lookup(dircache, &key)))
+        cdir = hnode_get(hn);
+
+    if (cdir) {
+        if (cdir->d_fullpath == NULL) { /* (1) */
+            LOG(log_debug, logtype_afpd, "dircache(cnid:%u): {not a directory:\"%s\"}",
+                ntohl(cnid), cfrombstr(cdir->d_u_name));
+            (void)dir_remove(vol, cdir); /* (1a) */
+            dircache_stat.expunged++;
+            return NULL;        /* (1b) */
+
+        }
+        if (lstat(cfrombstr(cdir->d_fullpath), &st) != 0) {
+            LOG(log_debug, logtype_afpd, "dircache(cnid:%u): {missing:\"%s\"}",
+                ntohl(cnid), cfrombstr(cdir->d_fullpath));
+            (void)dir_remove(vol, cdir);
+            dircache_stat.expunged++;
+            return NULL;
+        }
+        if (cdir->ctime_dircache != st.st_ctime) {
+            LOG(log_debug, logtype_afpd, "dircache(cnid:%u): {modified:\"%s\"}",
+                ntohl(cnid), cfrombstr(cdir->d_u_name));
+            (void)dir_remove(vol, cdir);
+            dircache_stat.expunged++;
+            return NULL;
+        }
+        LOG(log_debug, logtype_afpd, "dircache(cnid:%u): {cached: path:\"%s\"}",
+            ntohl(cnid), cfrombstr(cdir->d_fullpath));
+        dircache_stat.hits++;
+    } else {
+        LOG(log_debug, logtype_afpd, "dircache(cnid:%u): {not in cache}", ntohl(cnid));
+        dircache_stat.misses++;
+    }
+    
+    return cdir;
+}
+
+/*!
+ * @brief Search the cache via did/name hashtable
+ *
+ * Found cache entries are expunged if both the parent directory st_ctime and the objects
+ * st_ctime are modified.
+ *
+ * @param vol      (r) volume
+ * @param dir      (r) directory
+ * @param name     (r) name (server side encoding)
+ * @parma len      (r) strlen of name
+ * @param ctime    (r) current st_ctime from stat
+ *
+ * @returns pointer to struct dir if found in cache, else NULL
+ */
+struct dir *dircache_search_by_name(const struct vol *vol,
+                                    const struct dir *dir,
+                                    char *name,
+                                    int len,
+                                    time_t ctime)
+{
+    struct dir *cdir = NULL;
+    struct dir key;
+
+    hnode_t *hn;
+    static_bstring uname = {-1, len, (unsigned char *)name};
+
+    AFP_ASSERT(vol);
+    AFP_ASSERT(dir);
+    AFP_ASSERT(name);
+    AFP_ASSERT(len == strlen(name));
+    AFP_ASSERT(len < 256);
+
+    dircache_stat.lookups++;
+    LOG(log_debug, logtype_afpd, "dircache_search_by_name(did:%u, \"%s\")",
+        ntohl(dir->d_did), name);
+
+    if (dir->d_did != DIRDID_ROOT_PARENT) {
+        key.d_vid = vol->v_vid;
+        key.d_pdid = dir->d_did;
+        key.d_u_name = &uname;
+
+        if ((hn = hash_lookup(index_didname, &key)))
+            cdir = hnode_get(hn);
+    }
+
+    if (cdir) {
+        if (cdir->ctime_dircache != ctime) {
+            LOG(log_debug, logtype_afpd, "dircache(did:%u,\"%s\"): {modified}",
+                ntohl(dir->d_did), name);
+            (void)dir_remove(vol, cdir);
+            dircache_stat.expunged++;
+            return NULL;
+        }
+        LOG(log_debug, logtype_afpd, "dircache(did:%u,\"%s\"): {found in cache}",
+            ntohl(dir->d_did), name);
+        dircache_stat.hits++;
+    } else {
+        LOG(log_debug, logtype_afpd, "dircache(did:%u,\"%s\"): {not in cache}",
+            ntohl(dir->d_did), name);
+        dircache_stat.misses++;
+    }
+
+    return cdir;
+}
+
+/*!
+ * @brief create struct dir from struct path
+ *
+ * Add a struct dir to the cache and its indexes.
+ *
+ * @param dir   (r) pointer to parrent directory
+ *
+ * @returns 0 on success, -1 on error which should result in an abort
+ */
+int dircache_add(const struct vol *vol,
+                 struct dir *dir)
+{
+    struct dir *cdir = NULL;
+    struct dir key;
+    hnode_t *hn;
+
+    AFP_ASSERT(dir);
+    AFP_ASSERT(ntohl(dir->d_pdid) >= 2);
+    AFP_ASSERT(ntohl(dir->d_did) >= CNID_START);
+    AFP_ASSERT(dir->d_u_name);
+    AFP_ASSERT(dir->d_vid);
+    AFP_ASSERT(dircache->hash_nodecount <= dircache_maxsize);
+
+    /* Check if cache is full */
+    if (dircache->hash_nodecount == dircache_maxsize)
+        dircache_evict();
+
+    /* 
+     * Make sure we don't add duplicates
+     */
+
+    /* Search primary cache by CNID */
+    key.d_vid = dir->d_vid;
+    key.d_did = dir->d_did;
+    if ((hn = hash_lookup(dircache, &key))) {
+        /* Found an entry with the same CNID, delete it */
+        dir_remove(vol, hnode_get(hn));
+        dircache_stat.expunged++;
+    }
+    key.d_vid = vol->v_vid;
+    key.d_pdid = dir->d_did;
+    key.d_u_name = dir->d_u_name;
+    if ((hn = hash_lookup(index_didname, &key))) {
+        /* Found an entry with the same DID/name, delete it */
+        dir_remove(vol, hnode_get(hn));
+        dircache_stat.expunged++;
+    }
+
+    /* Add it to the main dircache */
+    if (hash_alloc_insert(dircache, dir, dir) == 0) {
+        dircache_dump();
+        exit(EXITERR_SYS);
+    }
+
+    /* Add it to the did/name index */
+    if (hash_alloc_insert(index_didname, dir, dir) == 0) {
+        dircache_dump();
+        exit(EXITERR_SYS);
+    }
+
+    /* Add it to the fifo queue index */
+    if ((dir->qidx_node = enqueue(index_queue, dir)) == NULL) {
+        dircache_dump();
+        exit(EXITERR_SYS);
+    } else {
+        queue_count++;
+    }
+
+    dircache_stat.added++;
+    LOG(log_debug, logtype_afpd, "dircache(did:%u,'%s'): {added}",
+        ntohl(dir->d_did), cfrombstr(dir->d_u_name));
+
+   AFP_ASSERT(queue_count == index_didname->hash_nodecount 
+           && queue_count == dircache->hash_nodecount);
+
+    return 0;
+}
+
+/*!
+  * @brief Remove an entry from the dircache
+  *
+  * Callers outside of dircache.c should call this with
+  * flags = QUEUE_INDEX | DIDNAME_INDEX | DIRCACHE.
+  */
+void dircache_remove(const struct vol *vol _U_, struct dir *dir, int flags)
+{
+    hnode_t *hn;
+
+    AFP_ASSERT(dir);
+    AFP_ASSERT((flags & ~(QUEUE_INDEX | DIDNAME_INDEX | DIRCACHE)) == 0);
+
+    if (flags & QUEUE_INDEX) {
+        /* remove it from the queue index */
+        dequeue(dir->qidx_node->prev); /* this effectively deletes the dequeued node */
+        queue_count--;
+    }
+
+    if (flags & DIDNAME_INDEX) {
+        if ((hn = hash_lookup(index_didname, dir)) == NULL) {
+            LOG(log_error, logtype_afpd, "dircache_remove(%u,\"%s\"): not in didname index", 
+                ntohl(dir->d_did), cfrombstr(dir->d_u_name));
+            dircache_dump();
+            AFP_PANIC("dircache_remove");
+        }
+        hash_delete_free(index_didname, hn);
+    }
+
+    if (flags & DIRCACHE) {
+        if ((hn = hash_lookup(dircache, dir)) == NULL) {
+            LOG(log_error, logtype_afpd, "dircache_remove(%u,\"%s\"): not in dircache", 
+                ntohl(dir->d_did), cfrombstr(dir->d_u_name));
+            dircache_dump();
+            AFP_PANIC("dircache_remove");
+        }
+        hash_delete_free(dircache, hn);
+    }
+
+    LOG(log_debug, logtype_afpd, "dircache(did:%u,\"%s\"): {removed}",
+        ntohl(dir->d_did), cfrombstr(dir->d_u_name));
+
+    dircache_stat.removed++;
+    AFP_ASSERT(queue_count == index_didname->hash_nodecount 
+               && queue_count == dircache->hash_nodecount);
+}
+
+/*!
+ * @brief Initialize the dircache and indexes
+ *
+ * This is called in child afpd initialisation. The maximum cache size will be
+ * max(DEFAULT_MAX_DIRCACHE_SIZE, min(size, MAX_POSSIBLE_DIRCACHE_SIZE)).
+ * It initializes a hashtable which we use to store a directory cache in.
+ * It also initializes two indexes:
+ * - a DID/name index on the main dircache
+ * - a queue index on the dircache
+ *
+ * @param size   (r) requested maximum size from afpd.conf
+ *
+ * @return 0 on success, -1 on error
+ */
+int dircache_init(int reqsize)
+{
+    dircache_maxsize = DEFAULT_MAX_DIRCACHE_SIZE;
+
+    /* Initialize the main dircache */
+    if (reqsize > DEFAULT_MAX_DIRCACHE_SIZE && reqsize < MAX_POSSIBLE_DIRCACHE_SIZE) {
+        while ((dircache_maxsize < MAX_POSSIBLE_DIRCACHE_SIZE) && (dircache_maxsize < reqsize))
+               dircache_maxsize *= 2;
+    }
+    if ((dircache = hash_create(dircache_maxsize, hash_comp_vid_did, hash_vid_did)) == NULL)
+        return -1;
+    
+    LOG(log_debug, logtype_afpd, "dircache_init: done. max dircache size: %u", dircache_maxsize);
+
+    /* Initialize did/name index hashtable */
+    if ((index_didname = hash_create(dircache_maxsize, hash_comp_didname, hash_didname)) == NULL)
+        return -1;
+
+    /* Initialize index queue */
+    if ((index_queue = queue_init()) == NULL)
+        return -1;
+    else
+        queue_count = 0;
+
+    /* Initialize index queue */
+    if ((invalid_dircache_entries = queue_init()) == NULL)
+        return -1;
+
+    /* As long as directory.c hasn't got its own initializer call, we do it for it */
+    rootParent.d_did = DIRDID_ROOT_PARENT;
+    rootParent.d_fullpath = bfromcstr("ROOT_PARENT");
+    rootParent.d_m_name = bfromcstr("ROOT_PARENT");
+    rootParent.d_u_name = rootParent.d_m_name;
+
+    return 0;
+}
+
+/*!
+ * Log dircache statistics
+ */
+void log_dircache_stat(void)
+{
+    LOG(log_info, logtype_afpd, "dircache statistics: "
+        "entries: %lu, lookups: %llu, hits: %llu, misses: %llu, added: %llu, removed: %llu, expunged: %llu, evicted: %llu",
+        queue_count,
+        dircache_stat.lookups,
+        dircache_stat.hits,
+        dircache_stat.misses,
+        dircache_stat.added,
+        dircache_stat.removed,
+        dircache_stat.expunged,
+        dircache_stat.evicted);
+}
+
+/*!
+ * @brief Dump dircache to /tmp/dircache.PID
+ */
+void dircache_dump(void)
+{
+    char tmpnam[64];
+    FILE *dump;
+    qnode_t *n = index_queue->next;
+    hnode_t *hn;
+    hscan_t hs;
+    const struct dir *dir;
+    int i;
+
+    LOG(log_warning, logtype_afpd, "Dumping directory cache...");
+
+    sprintf(tmpnam, "/tmp/dircache.%u", getpid());
+    if ((dump = fopen(tmpnam, "w+")) == NULL) {
+        LOG(log_error, logtype_afpd, "dircache_dump: %s", strerror(errno));
+        return;
+    }
+    setbuf(dump, NULL);
+
+    fprintf(dump, "Number of cache entries in LRU queue: %lu\n", queue_count);
+    fprintf(dump, "Configured maximum cache size: %u\n\n", dircache_maxsize);
+
+    fprintf(dump, "Primary CNID index:\n");
+    fprintf(dump, "       VID     DID    CNID STAT PATH\n");
+    fprintf(dump, "====================================================================\n");
+    hash_scan_begin(&hs, dircache);
+    i = 1;
+    while ((hn = hash_scan_next(&hs))) {
+        dir = hnode_get(hn);
+        fprintf(dump, "%05u: %3u  %6u  %6u %s    %s\n",
+                i++,
+                ntohs(dir->d_vid),
+                ntohl(dir->d_pdid),
+                ntohl(dir->d_did),
+                dir->d_fullpath ? "d" : "f",
+                cfrombstr(dir->d_u_name));
+    }
+
+    fprintf(dump, "\nSecondary DID/name index:\n");
+    fprintf(dump, "       VID     DID    CNID STAT PATH\n");
+    fprintf(dump, "====================================================================\n");
+    hash_scan_begin(&hs, index_didname);
+    i = 1;
+    while ((hn = hash_scan_next(&hs))) {
+        dir = hnode_get(hn);
+        fprintf(dump, "%05u: %3u  %6u  %6u %s    %s\n",
+                i++,
+                ntohs(dir->d_vid),
+                ntohl(dir->d_pdid),
+                ntohl(dir->d_did),
+                dir->d_fullpath ? "d" : "f",
+                cfrombstr(dir->d_u_name));
+    }
+
+    fprintf(dump, "\nLRU Queue:\n");
+    fprintf(dump, "       VID     DID    CNID STAT PATH\n");
+    fprintf(dump, "====================================================================\n");
+
+    for (i = 1; i <= queue_count; i++) {
+        if (n == index_queue)
+            break;
+        dir = (struct dir *)n->data;
+        fprintf(dump, "%05u: %3u  %6u  %6u %s    %s\n",
+                i,
+                ntohs(dir->d_vid),
+                ntohl(dir->d_pdid),
+                ntohl(dir->d_did),
+                dir->d_fullpath ? "d" : "f",
+                cfrombstr(dir->d_u_name));
+        n = n->next;
+    }
+
+    fprintf(dump, "\n");
+    fflush(dump);
+    return;
+}
diff --git a/etc/afpd/dircache.h b/etc/afpd/dircache.h
new file mode 100644 (file)
index 0000000..16c2df1
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+   Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+   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.
+ */
+
+#ifndef DIRCACHE_H 
+#define DIRCACHE_H
+
+#include <sys/types.h>
+
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+
+/* Maximum size of the dircache hashtable */
+#define DEFAULT_MAX_DIRCACHE_SIZE 8192
+#define MAX_POSSIBLE_DIRCACHE_SIZE 131072
+#define DIRCACHE_FREE_QUANTUM 256
+
+/* flags for dircache_remove */
+#define DIRCACHE      (1 << 0)
+#define DIDNAME_INDEX (1 << 1)
+#define QUEUE_INDEX   (1 << 2)
+#define DIRCACHE_ALL  (DIRCACHE|DIDNAME_INDEX|QUEUE_INDEX)
+
+extern int        dircache_init(int reqsize);
+extern int        dircache_add(const struct vol *, struct dir *);
+extern void       dircache_remove(const struct vol *, struct dir *, int flag);
+extern struct dir *dircache_search_by_did(const struct vol *vol, cnid_t did);
+extern struct dir *dircache_search_by_name(const struct vol *, const struct dir *dir, char *name, int len, time_t ctime);
+extern void       dircache_dump(void);
+extern void       log_dircache_stat(void);
+#endif /* DIRCACHE_H */
index 4544d28e153084897194e44f1286c0952f501aba..06ad94f3f10768e5fb9d380237c501ad14839b99 100644 (file)
@@ -1,42 +1,22 @@
 /*
- * $Id: directory.c,v 1.140 2010-03-12 15:16:49 franklahm Exp $
- *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
- *
- * 19 jan 2000 implemented red-black trees for directory lookups
- * (asun@cobalt.com).
  */
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
 
-/* STDC check */
-#if STDC_HEADERS
 #include <string.h>
-#else /* STDC_HEADERS */
-#ifndef HAVE_STRCHR
-#define strchr index
-#define strrchr index
-#endif /* HAVE_STRCHR */
-char *strchr (), *strrchr ();
-#ifndef HAVE_MEMCPY
-#define memcpy(d,s,n) bcopy ((s), (d), (n))
-#define memmove(d,s,n) bcopy ((s), (d), (n))
-#endif /* ! HAVE_MEMCPY */
-#endif /* STDC_HEADERS */
-#ifdef HAVE_STRINGS_H
-#include <strings.h>
-#endif
 #include <stdio.h>
 #include <stdlib.h>
-
 #include <grp.h>
 #include <pwd.h>
 #include <sys/param.h>
+#include <sys/stat.h>
 #include <errno.h>
 #include <utime.h>
+#include <assert.h>
 
 #include <atalk/adouble.h>
 #include <atalk/vfs.h>
@@ -46,8 +26,12 @@ char *strchr (), *strrchr ();
 #include <atalk/logger.h>
 #include <atalk/uuid.h>
 #include <atalk/unix.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
+#include <atalk/errchk.h>
 
 #include "directory.h"
+#include "dircache.h"
 #include "desktop.h"
 #include "volume.h"
 #include "fork.h"
@@ -59,583 +43,653 @@ char *strchr (), *strrchr ();
 #include "hash.h"
 #include "fce_api.h"
 
-#ifdef HAVE_NFSv4_ACLS
-extern void addir_inherit_acl(const struct vol *vol);
-#endif
-
-/* 
- * Directory caches
- * ================
- *
- * There are currently two cache structures where afpd caches directory information
- * a) a DID/dirname cache in a hashtable 
- * b) a (red-black) tree with CNIDs as key
- *
- * a) is for searching by DID/dirname
- * b) is for searching by CNID
- *
- * Through additional parent, child, previous and next pointers, b) is also used to
- * represent the on-disk layout of the filesystem. parent and child point to parent
- * and child directory respectively, linking 2 or more subdirectories in one
- * directory with previous and next pointers.
- *
- * Usage examples, highlighting the main functions:
- * 
- * a) is eg used in enumerate():
- * if IS_DIR
- *     dir = dirsearch_byname() // search in cache
- *     if (dir == NULL)         // not found
- *         dir = adddir()       // add to cache
- *      getdirparams()
- *
- * b) is eg used in afp_getfildirparams()
- * dirlookup()             // wrapper for cache and db search
- *   => dir = dirsearch()  // search in cache
- *      if (dir)           // found
- *          return
- *      else               // not found,
- *          cnid_resolve() // resolve with CNID database
- *      cname()            // add to cache
+/*
+ * FIXMEs, loose ends after the dircache rewrite:
+ * o merge dircache_search_by_name and dir_add ??
+ * o case-insensitivity is gone from cname
  */
 
-struct dir  *curdir;
-int         afp_errno;
-
-#define SENTINEL (&sentinel)
-static struct dir sentinel = { SENTINEL, SENTINEL, NULL, /* left, right, back */
-                               DIRTREE_COLOR_BLACK,      /* color */
-                               NULL, NULL,               /* parent, child */
-                               NULL, NULL,               /* previous, next */
-                               NULL, 0, 0,               /* oforks, did, flags */
-                               0, 0,                     /* ctime, offcnt */
-                               NULL, NULL, NULL};        /* mname, uname, ucs2-name */
-static struct dir rootpar  = { SENTINEL, SENTINEL, NULL,
-                               0,
-                               NULL, NULL,
-                               NULL, NULL,
-                               NULL, 0, 0,
-                               0, 0,
-                               NULL, NULL, NULL};
-
-/* (from IM: Toolbox Essentials)
- * dirFinderInfo (DInfo) fields:
- * field        bytes
- * frRect       8    folder's window rectangle
- * frFlags      2    flags
- * frLocation   4    folder's location in window
- * frView       2    folder's view (default == closedView (256))
- *
- * extended dirFinderInfo (DXInfo) fields:
- * frScroll     4    scroll position
- * frOpenChain: 4    directory ID chain of open folders
- * frScript:    1    script flag and code
- * frXFlags:    1    reserved
- * frComment:   2    comment ID
- * frPutAway:   4    home directory ID
- */
 
-static struct dir *
-vol_tree_root(const struct vol *vol, u_int32_t did)
-{
-    struct dir *dir;
+/*******************************************************************************************
+ * Globals
+ ******************************************************************************************/
 
-    if (vol->v_curdir && vol->v_curdir->d_did == did) {
-        dir = vol->v_curdir;
-    }
-    else {
-        dir = vol->v_root;
-    }
-    return dir;
-}
+int         afp_errno;
+/* As long as directory.c hasn't got its own init call, this get initialized in dircache_init */
+struct dir rootParent  = {
+    NULL, NULL, NULL, NULL,          /* path, d_m_name, d_u_name, d_m_name_ucs2 */
+    NULL, 0, 0,                      /* qidx_node, ctime, d_flags */
+    0, 0, 0, 0                       /* pdid, did, offcnt, d_vid */
+};
+struct dir  *curdir = &rootParent;
+struct path Cur_Path = {
+    0,
+    "",  /* mac name */
+    ".", /* unix name */
+    0,   /* id */
+    NULL,/* struct dir * */
+    0,   /* stat is not set */
+    0,   /* errno */
+    {0} /* struct stat */
+};
 
 /*
- * redid did assignment for directories. now we use red-black trees.
- * how exciting.
+ * dir_remove queues struct dirs to be freed here. We can't just delete them immeidately
+ * eg in dircache_search_by_id, because a caller somewhere up the stack might be
+ * referencing it.
+ * So instead:
+ * - we mark it as invalid by setting d_did to CNID_INVALID (ie 0)
+ * - queue it in "invalid_dircache_entries" queue
+ * - which is finally freed at the end of every AFP func in afp_dsi.c.
  */
-struct dir *
-dirsearch(const struct vol *vol, u_int32_t did)
-{
-    struct dir  *dir;
+q_t *invalid_dircache_entries;
 
 
-    /* check for 0 did */
-    if (!did) {
-        afp_errno = AFPERR_PARAM;
-        return NULL;
-    }
-    if ( did == DIRDID_ROOT_PARENT ) {
-        if (!rootpar.d_did)
-            rootpar.d_did = DIRDID_ROOT_PARENT;
-        rootpar.d_child = vol->v_dir;
-        return( &rootpar );
-    }
-
-    dir = vol_tree_root(vol, did);
+/*******************************************************************************************
+ * Locals
+ ******************************************************************************************/
 
-    afp_errno = AFPERR_NOOBJ;
-    while ( dir != SENTINEL ) {
-        if (dir->d_did == did)
-            return dir->d_m_name ? dir : NULL;
-        dir = (dir->d_did > did) ? dir->d_left : dir->d_right;
-    }
-    return NULL;
-}
 
-/* ------------------- */
-int get_afp_errno(const int param)
+/* -------------------------
+   appledouble mkdir afp error code.
+*/
+static int netatalk_mkdir(const struct vol *vol, const char *name)
 {
-    if (afp_errno != AFPERR_DID1)
-        return afp_errno;
-    return param;
-}
+    int ret;
+    struct stat st;
 
-/* ------------------- */
-struct dir *
-dirsearch_byname( const struct vol *vol, struct dir *cdir, char *name)
-{
-    struct dir *dir = NULL;
+    if (vol->v_flags & AFPVOL_UNIX_PRIV) {
+        if (lstat(".", &st) < 0)
+            return AFPERR_MISC;
+        int mode = (DIRBITS & (~S_ISGID & st.st_mode)) | (0777 & ~vol->v_umask);
+        LOG(log_maxdebug, logtype_afpd, "netatalk_mkdir(\"%s\") {parent mode: %04o, vol umask: %04o}",
+            name, st.st_mode, vol->v_umask);
 
-    if ((cdir->d_did != DIRDID_ROOT_PARENT) && (cdir->d_child)) {
-        struct dir key;
-        hnode_t *hn;
+        ret = mkdir(name, mode);
+    } else {
+        ret = ad_mkdir(name, DIRBITS | 0777);
+    }
 
-        key.d_parent = cdir;
-        key.d_u_name = name;
-        hn = hash_lookup(vol->v_hash, &key);
-        if (hn) {
-            dir = hnode_get(hn);
+    if (ret < 0) {
+        switch ( errno ) {
+        case ENOENT :
+            return( AFPERR_NOOBJ );
+        case EROFS :
+            return( AFPERR_VLOCK );
+        case EPERM:
+        case EACCES :
+            return( AFPERR_ACCESS );
+        case EEXIST :
+            return( AFPERR_EXIST );
+        case ENOSPC :
+        case EDQUOT :
+            return( AFPERR_DFULL );
+        default :
+            return( AFPERR_PARAM );
         }
     }
-    return dir;
+    return AFP_OK;
 }
 
-/* -----------------------------------------
- * if did is not in the cache resolve it with cnid
- *
- * FIXME
- * OSX call it with bogus id, ie file ID not folder ID,
- * and we are really bad in this case.
- */
-struct dir *
-dirlookup( struct vol *vol, u_int32_t did)
+/* ------------------- */
+static int deletedir(int dirfd, char *dir)
 {
-    struct dir   *ret;
-    char     *upath;
-    cnid_t       id, cnid;
-    static char  path[MAXPATHLEN + 1];
-    size_t len,  pathlen;
-    char         *ptr;
-    static char  buffer[12 + MAXPATHLEN + 1];
-    int          buflen = 12 + MAXPATHLEN + 1;
-    char         *mpath;
-    int          utf8;
-    size_t       maxpath;
+    char path[MAXPATHLEN + 1];
+    DIR *dp;
+    struct dirent   *de;
+    struct stat st;
+    size_t len;
+    int err = AFP_OK;
+    size_t remain;
 
-    ret = dirsearch(vol, did);
-    if (ret != NULL || afp_errno == AFPERR_PARAM)
-        return ret;
+    if ((len = strlen(dir)) +2 > sizeof(path))
+        return AFPERR_PARAM;
 
-    utf8 = utf8_encoding();
-    maxpath = (utf8)?MAXPATHLEN -7:255;
-    id = did;
-    if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen)) ) {
-        afp_errno = AFPERR_NOOBJ;
-        return NULL;
-    }
-    ptr = path + MAXPATHLEN;
-    if (NULL == ( mpath = utompath(vol, upath, did, utf8) ) ) {
-        afp_errno = AFPERR_NOOBJ;
-        return NULL;
-    }
-    len = strlen(mpath);
-    pathlen = len;          /* no 0 in the last part */
+    /* already gone */
+    if ((dp = opendirat(dirfd, dir)) == NULL)
+        return AFP_OK;
+
+    strcpy(path, dir);
+    strcat(path, "/");
     len++;
-    strcpy(ptr - len, mpath);
-    ptr -= len;
-    while (1) {
-        ret = dirsearch(vol,id);
-        if (ret != NULL) {
+    remain = sizeof(path) -len -1;
+    while ((de = readdir(dp)) && err == AFP_OK) {
+        /* skip this and previous directory */
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+            continue;
+
+        if (strlen(de->d_name) > remain) {
+            err = AFPERR_PARAM;
             break;
         }
-        cnid = id;
-        if ( NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen))
-             ||
-             NULL == (mpath = utompath(vol, upath, cnid, utf8))
-            ) {
-            afp_errno = AFPERR_NOOBJ;
-            return NULL;
+        strcpy(path + len, de->d_name);
+        if (lstatat(dirfd, path, &st)) {
+            continue;
         }
-
-        len = strlen(mpath) + 1;
-        pathlen += len;
-        if (pathlen > maxpath) {
-            afp_errno = AFPERR_PARAM;
-            return NULL;
+        if (S_ISDIR(st.st_mode)) {
+            err = deletedir(dirfd, path);
+        } else {
+            err = netatalk_unlinkat(dirfd, path);
         }
-        strcpy(ptr - len, mpath);
-        ptr -= len;
-    }
-
-    /* fill the cache, another place where we know about the path type */
-    if (utf8) {
-        u_int16_t temp16;
-        u_int32_t temp;
-
-        ptr -= 2;
-        temp16 = htons(pathlen);
-        memcpy(ptr, &temp16, sizeof(temp16));
-
-        temp = htonl(kTextEncodingUTF8);
-        ptr -= 4;
-        memcpy(ptr, &temp, sizeof(temp));
-        ptr--;
-        *ptr = 3;
-    }
-    else {
-        ptr--;
-        *ptr = (unsigned char)pathlen;
-        ptr--;
-        *ptr = 2;
     }
-    /* cname is not efficient */
-    if (cname( vol, ret, &ptr ) == NULL )
-        return NULL;
-
-    return dirsearch(vol, did);
-}
+    closedir(dp);
 
-/* child addition/removal */
-static void dirchildadd(const struct vol *vol, struct dir *a, struct dir *b)
-{
-    if (!a->d_child)
-        a->d_child = b;
-    else {
-        b->d_next = a->d_child;
-        b->d_prev = b->d_next->d_prev;
-        b->d_next->d_prev = b;
-        b->d_prev->d_next = b;
-    }
-    if (!hash_alloc_insert(vol->v_hash, b, b)) {
-        LOG(log_error, logtype_afpd, "dirchildadd: can't hash %s", b->d_u_name);
+    /* okay. the directory is empty. delete it. note: we already got rid
+       of .AppleDouble.  */
+    if (err == AFP_OK) {
+        err = netatalk_rmdir(dirfd, dir);
     }
+    return err;
 }
 
-static void dirchildremove(struct dir *a,struct dir *b)
-{
-    if (a->d_child == b)
-        a->d_child = (b == b->d_next) ? NULL : b->d_next;
-    b->d_next->d_prev = b->d_prev;
-    b->d_prev->d_next = b->d_next;
-    b->d_next = b->d_prev = b;
-}
-
-/* --------------------------- */
-/* rotate the tree to the left */
-static void dir_leftrotate(struct vol *vol, struct dir *dir)
+/* do a recursive copy. */
+static int copydir(const struct vol *vol, int dirfd, char *src, char *dst)
 {
-    struct dir *right = dir->d_right;
+    char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
+    DIR *dp;
+    struct dirent   *de;
+    struct stat st;
+    struct utimbuf      ut;
+    size_t slen, dlen;
+    size_t srem, drem;
+    int err;
 
-    /* whee. move the right's left tree into dir's right tree */
-    dir->d_right = right->d_left;
-    if (right->d_left != SENTINEL)
-        right->d_left->d_back = dir;
+    /* doesn't exist or the path is too long. */
+    if (((slen = strlen(src)) > sizeof(spath) - 2) ||
+        ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
+        ((dp = opendirat(dirfd, src)) == NULL))
+        return AFPERR_PARAM;
 
-    if (right != SENTINEL) {
-        right->d_back = dir->d_back;
-        right->d_left = dir;
+    /* try to create the destination directory */
+    if (AFP_OK != (err = netatalk_mkdir(vol, dst)) ) {
+        closedir(dp);
+        return err;
     }
 
-    if (!dir->d_back) /* no parent. move the right tree to the top. */
-        vol->v_root = right;
-    else if (dir == dir->d_back->d_left) /* we were on the left */
-        dir->d_back->d_left = right;
-    else
-        dir->d_back->d_right = right; /* we were on the right */
-
-    /* re-insert dir on the left tree */
-    if (dir != SENTINEL)
-        dir->d_back = right;
-}
-
-
-
-/* rotate the tree to the right */
-static void dir_rightrotate(struct vol *vol, struct dir *dir)
-{
-    struct dir *left = dir->d_left;
-
-    /* whee. move the left's right tree into dir's left tree */
-    dir->d_left = left->d_right;
-    if (left->d_right != SENTINEL)
-        left->d_right->d_back = dir;
+    /* set things up to copy */
+    strcpy(spath, src);
+    strcat(spath, "/");
+    slen++;
+    srem = sizeof(spath) - slen -1;
 
-    if (left != SENTINEL) {
-        left->d_back = dir->d_back;
-        left->d_right = dir;
-    }
+    strcpy(dpath, dst);
+    strcat(dpath, "/");
+    dlen++;
+    drem = sizeof(dpath) - dlen -1;
 
-    if (!dir->d_back) /* no parent. move the left tree to the top. */
-        vol->v_root = left;
-    else if (dir == dir->d_back->d_right) /* we were on the right */
-        dir->d_back->d_right = left;
-    else
-        dir->d_back->d_left = left; /* we were on the left */
+    err = AFP_OK;
+    while ((de = readdir(dp))) {
+        /* skip this and previous directory */
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+            continue;
 
-    /* re-insert dir on the right tree */
-    if (dir != SENTINEL)
-        dir->d_back = left;
-}
+        if (strlen(de->d_name) > srem) {
+            err = AFPERR_PARAM;
+            break;
+        }
+        strcpy(spath + slen, de->d_name);
 
-#if 0
-/* recolor after a removal */
-static struct dir *dir_rmrecolor(struct vol *vol, struct dir *dir)
-{
-    struct dir *leaf;
-
-    while ((dir != vol->v_root) && (dir->d_color == DIRTREE_COLOR_BLACK)) {
-        /* are we on the left tree? */
-        if (dir == dir->d_back->d_left) {
-            leaf = dir->d_back->d_right; /* get right side */
-            if (leaf->d_color == DIRTREE_COLOR_RED) {
-                /* we're red. we need to change to black. */
-                leaf->d_color = DIRTREE_COLOR_BLACK;
-                dir->d_back->d_color = DIRTREE_COLOR_RED;
-                dir_leftrotate(vol, dir->d_back);
-                leaf = dir->d_back->d_right;
+        if (lstatat(dirfd, spath, &st) == 0) {
+            if (strlen(de->d_name) > drem) {
+                err = AFPERR_PARAM;
+                break;
             }
+            strcpy(dpath + dlen, de->d_name);
 
-            /* right leaf has black end nodes */
-            if ((leaf->d_left->d_color == DIRTREE_COLOR_BLACK) &&
-                (leaf->d_right->d_color = DIRTREE_COLOR_BLACK)) {
-                leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
-                dir = dir->d_back; /* ascend */
-            } else {
-                if (leaf->d_right->d_color == DIRTREE_COLOR_BLACK) {
-                    leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
-                    leaf->d_color = DIRTREE_COLOR_RED;
-                    dir_rightrotate(vol, leaf);
-                    leaf = dir->d_back->d_right;
-                }
-                leaf->d_color = dir->d_back->d_color;
-                dir->d_back->d_color = DIRTREE_COLOR_BLACK;
-                leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
-                dir_leftrotate(vol, dir->d_back);
-                dir = vol->v_root;
-            }
-        } else { /* right tree */
-            leaf = dir->d_back->d_left; /* left tree */
-            if (leaf->d_color == DIRTREE_COLOR_RED) {
-                leaf->d_color = DIRTREE_COLOR_BLACK;
-                dir->d_back->d_color = DIRTREE_COLOR_RED;
-                dir_rightrotate(vol, dir->d_back);
-                leaf = dir->d_back->d_left;
-            }
+            if (S_ISDIR(st.st_mode)) {
+                if (AFP_OK != (err = copydir(vol, dirfd, spath, dpath)))
+                    goto copydir_done;
+            } else if (AFP_OK != (err = copyfile(vol, vol, dirfd, spath, dpath, NULL, NULL))) {
+                goto copydir_done;
 
-            /* left leaf has black end nodes */
-            if ((leaf->d_right->d_color == DIRTREE_COLOR_BLACK) &&
-                (leaf->d_left->d_color = DIRTREE_COLOR_BLACK)) {
-                leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
-                dir = dir->d_back; /* ascend */
             } else {
-                if (leaf->d_left->d_color == DIRTREE_COLOR_BLACK) {
-                    leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
-                    leaf->d_color = DIRTREE_COLOR_RED;
-                    dir_leftrotate(vol, leaf);
-                    leaf = dir->d_back->d_left;
-                }
-                leaf->d_color = dir->d_back->d_color;
-                dir->d_back->d_color = DIRTREE_COLOR_BLACK;
-                leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
-                dir_rightrotate(vol, dir->d_back);
-                dir = vol->v_root;
+                /* keep the same time stamp. */
+                ut.actime = ut.modtime = st.st_mtime;
+                utime(dpath, &ut);
             }
         }
     }
-    dir->d_color = DIRTREE_COLOR_BLACK;
 
-    return dir;
+    /* keep the same time stamp. */
+    if (lstatat(dirfd, src, &st) == 0) {
+        ut.actime = ut.modtime = st.st_mtime;
+        utime(dst, &ut);
+    }
+
+copydir_done:
+    closedir(dp);
+    return err;
 }
-#endif /* 0 */
 
-/* --------------------- */
-static void dir_hash_del(const struct vol *vol, struct dir *dir)
+/* ---------------------
+ * is our cached offspring count valid?
+ */
+static int diroffcnt(struct dir *dir, struct stat *st)
 {
-    hnode_t *hn;
-
-    hn = hash_lookup(vol->v_hash, dir);
-    if (!hn) {
-        LOG(log_error, logtype_afpd, "dir_hash_del: %s not hashed", dir->d_u_name);
-    }
-    else {
-        hash_delete(vol->v_hash, hn);
-    }
+    return st->st_ctime == dir->ctime;
 }
 
-/* remove the node from the tree. this is just like insertion, but
- * different. actually, it has to worry about a bunch of things that
- * insertion doesn't care about. */
-
-static void dir_remove( struct vol *vol, struct dir *dir)
+/* --------------------- */
+static int invisible_dots(const struct vol *vol, const char *name)
 {
-#ifdef REMOVE_NODES
-    struct ofork *of, *last;
-    struct dir *node, *leaf;
-#endif /* REMOVE_NODES */
-
-    if (!dir || (dir == SENTINEL))
-        return;
-
-    /* i'm not sure if it really helps to delete stuff. */
-    dir_hash_del(vol, dir);
-    vol->v_curdir = NULL;
-#ifndef REMOVE_NODES
-    dirfreename(dir);
-    dir->d_m_name = NULL;
-    dir->d_u_name = NULL;
-    dir->d_m_name_ucs2 = NULL;
-#else /* ! REMOVE_NODES */
-
-    /* go searching for a node with at most one child */
-    if ((dir->d_left == SENTINEL) || (dir->d_right == SENTINEL)) {
-        node = dir;
-    } else {
-        node = dir->d_right;
-        while (node->d_left != SENTINEL)
-            node = node->d_left;
-    }
-
-    /* get that child */
-    leaf = (node->d_left != SENTINEL) ? node->d_left : node->d_right;
+    return vol_inv_dots(vol) && *name  == '.' && strcmp(name, ".") && strcmp(name, "..");
+}
 
-    /* detach node */
-    leaf->d_back = node->d_back;
-    if (!node->d_back) {
-        vol->v_root = leaf;
-    } else if (node == node->d_back->d_left) { /* left tree */
-        node->d_back->d_left = leaf;
-    } else {
-        node->d_back->d_right = leaf;
+/* ------------------ */
+static int set_dir_errors(struct path *path, const char *where, int err)
+{
+    switch ( err ) {
+    case EPERM :
+    case EACCES :
+        return AFPERR_ACCESS;
+    case EROFS :
+        return AFPERR_VLOCK;
+    }
+    LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
+    return AFPERR_PARAM;
+}
+
+/*!
+ * @brief Convert name in client encoding to server encoding
+ *
+ * Convert ret->m_name to ret->u_name from client encoding to server encoding.
+ * This only gets called from cname().
+ *
+ * @returns 0 on success, -1 on error
+ *
+ * @note If the passed ret->m_name is mangled, we'll demangle it
+ */
+static int cname_mtouname(const struct vol *vol, const struct dir *dir, struct path *ret, int toUTF8)
+{
+    static char temp[ MAXPATHLEN + 1];
+    char *t;
+    cnid_t fileid = 0;
+
+    if (afp_version >= 30) {
+        if (toUTF8) {
+            if (dir->d_did == DIRDID_ROOT_PARENT) {
+                /*
+                 * With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
+                 * So we compare it with the longname from the current volume and if they match
+                 * we overwrite the requested path with the utf8 volume name so that the following
+                 * strcmp can match.
+                 */
+                ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
+                if (strcasecmp(ret->m_name, temp) == 0)
+                    ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, ret->m_name, AFPVOL_U8MNAMELEN);
+            } else {
+                /* toUTF8 */
+                if (mtoUTF8(vol, ret->m_name, strlen(ret->m_name), temp, MAXPATHLEN) == (size_t)-1) {
+                    afp_errno = AFPERR_PARAM;
+                    return -1;
+                }
+                strcpy(ret->m_name, temp);
+            }
+        }
+
+        /* check for OS X mangled filename :( */
+        t = demangle_osx(vol, ret->m_name, dir->d_did, &fileid);
+        LOG(log_maxdebug, logtype_afpd, "cname_mtouname('%s',did:%u) {demangled:'%s', fileid:%u}",
+            ret->m_name, ntohl(dir->d_did), t, ntohl(fileid));
+
+        if (t != ret->m_name) {
+            ret->u_name = t;
+            /* duplicate work but we can't reuse all convert_char we did in demangle_osx
+             * flags weren't the same
+             */
+            if ( (t = utompath(vol, ret->u_name, fileid, utf8_encoding())) ) {
+                /* at last got our view of mac name */
+                strcpy(ret->m_name, t);
+            }
+        }
+    } /* afp_version >= 30 */
+
+    /* If we haven't got it by now, get it */
+    if (ret->u_name == NULL) {
+        if ((ret->u_name = mtoupath(vol, ret->m_name, dir->d_did, utf8_encoding())) == NULL) {
+            afp_errno = AFPERR_PARAM;
+            return -1;
+        }
     }
 
-    /* we want to free node, but we also want to free the data in dir.
-     * currently, that's d_name and the directory traversal bits.
-     * we just copy the necessary bits and then fix up all the
-     * various pointers to the directory. needless to say, there are
-     * a bunch of places that store the directory struct. */
-    if (node != dir) {
-        struct dir save, *tmp;
+    return 0;
+}
 
-        memcpy(&save, dir, sizeof(save));
-        memcpy(dir, node, sizeof(struct dir));
+/*!
+ * @brief Build struct path from struct dir
+ *
+ * The final movecwd in cname failed, possibly with EPERM or ENOENT. We:
+ * 1. move cwd into parent dir (we're often already there, but not always)
+ * 2. set struct path to the dirname
+ * 3. in case of
+ *    AFPERR_ACCESS: the dir is there, we just cant chdir into it
+ *    AFPERR_NOOBJ: the dir was there when we stated it in cname, so we have a race
+ *                  4. indicate there's no dir for this path
+ *                  5. remove the dir
+ */
+static struct path *path_from_dir(struct vol *vol, struct dir *dir, struct path *ret)
+{
+    if (dir->d_did == DIRDID_ROOT_PARENT || dir->d_did == DIRDID_ROOT)
+        return NULL;
 
-        /* restore the red-black bits */
-        dir->d_left = save.d_left;
-        dir->d_right = save.d_right;
-        dir->d_back = save.d_back;
-        dir->d_color = save.d_color;
+    switch (afp_errno) {
 
-        if (node == vol->v_dir) {/* we may need to fix up this pointer */
-            vol->v_dir = dir;
-            rootpar.d_child = vol->v_dir;
+    case AFPERR_ACCESS:
+        if (movecwd( vol, dirlookup(vol, dir->d_pdid)) < 0 ) /* 1 */
+            return NULL;
+
+        memcpy(ret->m_name, cfrombstr(dir->d_m_name), blength(dir->d_m_name) + 1); /* 3 */
+        if (dir->d_m_name == dir->d_u_name) {
+            ret->u_name = ret->m_name;
         } else {
-            /* if we aren't the root directory, we have parents and
-             * siblings to worry about */
-            if (dir->d_parent->d_child == node)
-                dir->d_parent->d_child = dir;
-            dir->d_next->d_prev = dir;
-            dir->d_prev->d_next = dir;
+            ret->u_name =  ret->m_name + blength(dir->d_m_name) + 1;
+            memcpy(ret->u_name, cfrombstr(dir->d_u_name), blength(dir->d_u_name) + 1);
         }
 
-        /* fix up children. */
-        tmp = dir->d_child;
-        while (tmp) {
-            tmp->d_parent = dir;
-            tmp = (tmp == dir->d_child->d_prev) ? NULL : tmp->d_next;
-        }
+        ret->d_dir = dir;
 
-        if (node == curdir) /* another pointer to fixup */
-            curdir = dir;
+        LOG(log_debug, logtype_afpd, "cname('%s') {path-from-dir: AFPERR_ACCESS. curdir:'%s', path:'%s'}",
+            cfrombstr(dir->d_fullpath),
+            cfrombstr(curdir->d_fullpath),
+            ret->u_name);
 
-        /* we also need to fix up oforks. bleah */
-        if ((of = dir->d_ofork)) {
-            last = of->of_d_prev;
-            while (of) {
-                of->of_dir = dir;
-                of = (last == of) ? NULL : of->of_d_next;
-            }
+        return ret;
+
+    case AFPERR_NOOBJ:
+        if (movecwd(vol, dirlookup(vol, dir->d_pdid)) < 0 ) /* 1 */
+            return NULL;
+
+        memcpy(ret->m_name, cfrombstr(dir->d_m_name), blength(dir->d_m_name) + 1);
+        if (dir->d_m_name == dir->d_u_name) {
+            ret->u_name = ret->m_name;
+        } else {
+            ret->u_name =  ret->m_name + blength(dir->d_m_name) + 1;
+            memcpy(ret->u_name, cfrombstr(dir->d_u_name), blength(dir->d_u_name) + 1);
         }
 
-        /* set the node's d_name */
-        node->d_m_name = save.d_m_name;
-        node->d_u_name = save.d_u_name;
-        node->d_m_name_ucs2 = save.d_m_name_ucs2;
+        ret->d_dir = NULL;      /* 4 */
+        dir_remove(vol, dir);   /* 5 */
+        return ret;
+
+    default:
+        return NULL;
     }
 
-    if (node->d_color == DIRTREE_COLOR_BLACK)
-        dir_rmrecolor(vol, leaf);
+    /* DEADC0DE: never get here */
+    return NULL;
+}
 
-    if (node->d_m_name_ucs2)
-        free(node->d_u_name_ucs2);
-    if (node->d_u_name != node->d_m_name) {
-        free(node->d_u_name);
-    }
-    free(node->d_m_name);
-    free(node);
-#endif /* ! REMOVE_NODES */
+
+/*********************************************************************************************
+ * Interface
+ ********************************************************************************************/
+
+int get_afp_errno(const int param)
+{
+    if (afp_errno != AFPERR_DID1)
+        return afp_errno;
+    return param;
 }
 
-/* ---------------------------------------
- * remove the node and its childs from the tree
+/*!
+ * Resolve struct dir for an absolute path
+ *
+ * Given a path like "/Volumes/volume/dir/subdir" in a volume "/Volumes/volume" return
+ * a pointer to struct dir of "subdir".
+ * 1. Remove volue path from absolute path
+ * 2. start path
+ * 3. loop through all elements of the remaining path from 1.
+ * 4. we only allow dirs
+ * 5. search dircache
+ * 6. if not found in the dircache query the CNID database for the DID
+ * 7. and use dirlookup to resolve the DID to a it's struct dir *
  *
- * FIXME what about opened forks with refs to it?
- * it's an afp specs violation because you can't delete
- * an opened forks. Now afpd doesn't care about forks opened by other
- * process. It's fixable within afpd if fnctl_lock, doable with smb and
- * next to impossible for nfs and local filesystem access.
+ * @param vol   (r) volume the path is in, must be known
+ * @param path  (r) absoule path
+ *
+ * @returns pointer to struct dir or NULL on error
  */
-static void dir_invalidate( struct vol *vol, struct dir *dir)
+struct dir *dirlookup_bypath(const struct vol *vol, const char *path)
 {
-    if (curdir == dir) {
-        /* v_root can't be deleted */
-        if (movecwd(vol, vol->v_root) < 0) {
-            LOG(log_error, logtype_afpd, "cname can't chdir to : %s", vol->v_root);
+    EC_INIT;
+
+    struct dir *dir = NULL;
+    cnid_t cnid, did;
+    bstring rpath = NULL;
+    bstring statpath = NULL;
+    struct bstrList *l = NULL;
+    struct stat st;
+
+    cnid = htonl(2);
+    dir = vol->v_root;
+
+    EC_NULL(rpath = rel_path_in_vol(path, vol->v_path)); /* 1. */
+    EC_NULL(statpath = bfromcstr(vol->v_path));          /* 2. */
+
+    l = bsplit(rpath, '/');
+    for (int i = 0; i < l->qty ; i++) {                  /* 3. */
+        did = cnid;
+        EC_ZERO(bconcat(statpath, l->entry[i]));
+        EC_ZERO_LOGSTR(lstat(cfrombstr(statpath), &st),
+                       "lstat(rpath: %s, elem: %s): %s: %s",
+                       cfrombstr(rpath), cfrombstr(l->entry[i]),
+                       cfrombstr(statpath), strerror(errno));
+
+        if (!(S_ISDIR(st.st_mode)))                      /* 4. */
+            EC_FAIL;
+
+        if ((dir = dircache_search_by_name(vol,          /* 5. */
+                                           dir,
+                                           cfrombstr(l->entry[i]),
+                                           blength(l->entry[i]),
+                                           st.st_ctime)) == NULL) {
+            if ((cnid = cnid_add(vol->v_cdb,             /* 6. */
+                                 &st,
+                                 did,
+                                 cfrombstr(l->entry[i]),
+                                 blength(l->entry[i]),
+                                 0)) == CNID_INVALID) {
+                EC_FAIL;
+            }
+
+            if ((dir = dirlookup(vol, cnid)) == NULL) /* 7. */
+                EC_FAIL;
         }
+
+        EC_ZERO(bcatcstr(statpath, "/"));
     }
-    /* FIXME */
-    dirchildremove(dir->d_parent, dir);
-    dir_remove( vol, dir );
+
+EC_CLEANUP:
+    bdestroy(rpath);
+    bstrListDestroy(l);
+    bdestroy(statpath);
+    if (ret != 0)
+        return NULL;
+
+    return dir;
 }
 
-/* ------------------------------------ */
-static struct dir *dir_insert(const struct vol *vol, struct dir *dir)
+/*!
+ * @brief Resolve a DID
+ *
+ * Resolve a DID, allocate a struct dir for it
+ * 1. Check for special CNIDs 0 (invalid), 1 and 2.
+ * 2a. Check if the DID is in the cache.
+ * 2b. Check if it's really a dir (d_fullpath != NULL) because we cache files too.
+ * 3. If it's not in the cache resolve it via the database.
+ * 4. Build complete server-side path to the dir.
+ * 5. Check if it exists and is a directory.
+ * 6. Create the struct dir and populate it.
+ * 7. Add it to the cache.
+ *
+ * @param vol   (r) pointer to struct vol
+ * @param did   (r) DID to resolve
+ *
+ * @returns pointer to struct dir
+ */
+struct dir *dirlookup(const struct vol *vol, cnid_t did)
 {
-    struct dir  *pdir;
-
-    pdir = vol_tree_root(vol, dir->d_did);
-    while (pdir->d_did != dir->d_did ) {
-        if ( pdir->d_did > dir->d_did ) {
-            if ( pdir->d_left == SENTINEL ) {
-                pdir->d_left = dir;
-                dir->d_back = pdir;
-                return NULL;
-            }
-            pdir = pdir->d_left;
-        } else {
-            if ( pdir->d_right == SENTINEL ) {
-                pdir->d_right = dir;
-                dir->d_back = pdir;
-                return NULL;
+    static char  buffer[12 + MAXPATHLEN + 1];
+    struct stat  st;
+    struct dir   *ret = NULL, *pdir;
+    bstring      fullpath = NULL;
+    char         *upath = NULL, *mpath;
+    cnid_t       cnid, pdid;
+    size_t       maxpath;
+    int          buflen = 12 + MAXPATHLEN + 1;
+    int          utf8;
+    int          err = 0;
+
+    LOG(log_debug, logtype_afpd, "dirlookup(did: %u)", ntohl(did));
+
+    /* check for did 0, 1 and 2 */
+    if (did == 0 || vol == NULL) { /* 1 */
+        afp_errno = AFPERR_PARAM;
+        ret = NULL;
+        goto exit;
+    } else if (did == DIRDID_ROOT_PARENT) {
+        rootParent.d_vid = vol->v_vid;
+        ret = &rootParent;
+        goto exit;
+    } else if (did == DIRDID_ROOT) {
+        ret = vol->v_root;
+        goto exit;
+    }
+
+    /* Search the cache */
+    if ((ret = dircache_search_by_did(vol, did)) != NULL) { /* 2a */
+        if (ret->d_fullpath == NULL) {                      /* 2b */
+            afp_errno = AFPERR_BADTYPE;
+            ret = NULL;
+            goto exit;
+        }
+        if (lstat(cfrombstr(ret->d_fullpath), &st) != 0) {
+            LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {lstat: %s}", ntohl(did), strerror(errno));
+            switch (errno) {
+            case ENOENT:
+            case ENOTDIR:
+                /* It's not there anymore, so remove it */
+                LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {calling dir_remove()}", ntohl(did));
+                dir_remove(vol, ret);
+                afp_errno = AFPERR_NOOBJ;
+                ret = NULL;
+                goto exit;
+            default:
+                ret = ret;
+                goto exit;
             }
-            pdir = pdir->d_right;
+            /* DEADC0DE */
+            ret = NULL;
+            goto exit;            
+        }
+        ret = ret;
+        goto exit;
+    }
+
+    utf8 = utf8_encoding();
+    maxpath = (utf8) ? MAXPATHLEN - 7 : 255;
+
+    /* Get it from the database */
+    cnid = did;
+    if ((upath = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL) {
+        afp_errno = AFPERR_NOOBJ;
+        err = 1;
+        goto exit;
+    }
+    if ((upath = strdup(upath)) == NULL) { /* 3 */
+        afp_errno = AFPERR_NOOBJ;
+        err = 1;
+        goto exit;
+    }
+    pdid = cnid;
+
+    /*
+     * Recurse up the tree, terminates in dirlookup when either
+     * - DIRDID_ROOT is hit
+     * - a cached entry is found
+     */
+    LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {recursion for did: %u}", ntohl(pdid));
+    if ((pdir = dirlookup(vol, pdid)) == NULL) {
+        err = 1;
+        goto exit;
+    }
+
+    /* build the fullpath */
+    if ((fullpath = bstrcpy(pdir->d_fullpath)) == NULL
+        || bconchar(fullpath, '/') != BSTR_OK
+        || bcatcstr(fullpath, upath) != BSTR_OK) {
+        err = 1;
+        goto exit;
+    }
+
+    /* stat it and check if it's a dir */
+    LOG(log_debug, logtype_afpd, "dirlookup: {stating %s}", cfrombstr(fullpath));
+
+    if (stat(cfrombstr(fullpath), &st) != 0) { /* 5a */
+        switch (errno) {
+        case ENOENT:
+            afp_errno = AFPERR_NOOBJ;
+            err = 1;
+            goto exit;
+        case EPERM:
+            afp_errno = AFPERR_ACCESS;
+            err = 1;
+            goto exit;
+        default:
+            afp_errno = AFPERR_MISC;
+            err = 1;
+            goto exit;
+        }
+    } else {
+        if ( ! S_ISDIR(st.st_mode)) { /* 5b */
+            afp_errno = AFPERR_BADTYPE;
+            err = 1;
+            goto exit;
+        }
+    }
+
+    /* Get macname from unix name */
+    if ( (mpath = utompath(vol, upath, did, utf8)) == NULL ) {
+        afp_errno = AFPERR_NOOBJ;
+        err = 1;
+        goto exit;
+    }
+
+    /* Create struct dir */
+    if ((ret = dir_new(mpath, upath, vol, pdid, did, fullpath, st.st_ctime)) == NULL) { /* 6 */
+        LOG(log_error, logtype_afpd, "dirlookup(did: %u) {%s, %s}: %s", ntohl(did), mpath, upath, strerror(errno));
+        err = 1;
+        goto exit;
+    }
+    
+    /* Add it to the cache only if it's a dir */
+    if (dircache_add(vol, ret) != 0) { /* 7 */
+        err = 1;
+        goto exit;
+    }
+
+exit:
+    if (upath) free(upath);
+    if (err) {
+        LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {exit_error: %s}",
+            ntohl(did), AfpErr2name(afp_errno));
+        if (fullpath)
+            bdestroy(fullpath);
+        if (ret) {
+            dir_free(ret);
+            ret = NULL;
         }
     }
-    return pdir;
+    if (ret)
+        LOG(log_debug, logtype_afpd, "dirlookup(did: %u): pdid: %u, \"%s\"",
+            ntohl(ret->d_did), ntohl(ret->d_pdid), cfrombstr(ret->d_fullpath));
+
+    return ret;
 }
 
 #define ENUMVETO "./../Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/:2eDS_Store/Contents/Desktop Folder/Trash/Benutzer/"
 
-int
-caseenumerate(const struct vol *vol, struct path *path, struct dir *dir)
+int caseenumerate(const struct vol *vol, struct path *path, struct dir *dir)
 {
     DIR               *dp;
     struct dirent     *de;
@@ -715,635 +769,370 @@ caseenumerate(const struct vol *vol, struct path *path, struct dir *dir)
 }
 
 
-/*
- * attempt to extend the current dir. tree to include path
- * as a side-effect, movecwd to that point and return the new dir
+/*!
+ * @brief Construct struct dir
+ *
+ * Construct struct dir from parameters.
+ *
+ * @param m_name   (r) directory name in UTF8-dec
+ * @param u_name   (r) directory name in server side encoding
+ * @param vol      (r) pointer to struct vol
+ * @param pdid     (r) Parent CNID
+ * @param did      (r) CNID
+ * @param path     (r) Full unix path to dir or NULL for files
+ * @param ctime    (r) st_ctime from stat
+ *
+ * @returns pointer to new struct dir or NULL on error
+ *
+ * @note Most of the time mac name and unix name are the same.
  */
-static struct dir *
-extenddir(struct vol *vol, struct dir *dir, struct path *path)
+struct dir *dir_new(const char *m_name,
+                    const char *u_name,
+                    const struct vol *vol,
+                    cnid_t pdid,
+                    cnid_t did,
+                    bstring path,
+                    time_t ctime)
 {
-    path->d_dir = NULL;
+    struct dir *dir;
 
-    if ( path->u_name == NULL) {
-        afp_errno = AFPERR_PARAM;
+    dir = (struct dir *) calloc(1, sizeof( struct dir ));
+    if (!dir)
         return NULL;
-    }
 
-    if (check_name(vol, path->u_name)) {
-        /* the name is illegal */
-        LOG(log_info, logtype_afpd, "extenddir: illegal path: '%s'", path->u_name);
-        path->u_name = NULL;
-        afp_errno = AFPERR_PARAM;
+    if ((dir->d_m_name = bfromcstr(m_name)) == NULL) {
+        free(dir);
         return NULL;
     }
 
-    if (of_stat( path ) != 0 ) {
-        if (!(vol->v_flags & AFPVOL_CASEINSEN))
-            return NULL;
-        else if(caseenumerate(vol, path, dir) != 0)
-            return(NULL);
-    }
-
-    if (!S_ISDIR(path->st.st_mode)) {
-        return( NULL );
-    }
-
-    /* mac name is always with the right encoding (from cname()) */
-    if (( dir = adddir( vol, dir, path)) == NULL ) {
-        return( NULL );
-    }
-
-    path->d_dir = dir;
-    if ( movecwd( vol, dir ) < 0 ) {
-        return( NULL );
-    }
-
-    return( dir );
-}
-
-/* -------------------------
-   appledouble mkdir afp error code.
-*/
-static int netatalk_mkdir(const struct vol *vol, const char *name)
-{
-    int ret;
-    struct stat st;
-
-    if (vol->v_flags & AFPVOL_UNIX_PRIV) {
-        if (lstat(".", &st) < 0)
-            return AFPERR_MISC;
-        int mode = (DIRBITS & (~S_ISGID & st.st_mode)) | (0777 & ~vol->v_umask);
-        LOG(log_maxdebug, logtype_afpd, "netatalk_mkdir(\"%s\") {parent mode: %04o, vol umask: %04o}",
-            name, st.st_mode, vol->v_umask);
-
-        ret = mkdir(name, mode);
-    } else {
-        ret = ad_mkdir(name, DIRBITS | 0777);
-    }
-
-    if (ret < 0) {
-        switch ( errno ) {
-        case ENOENT :
-            return( AFPERR_NOOBJ );
-        case EROFS :
-            return( AFPERR_VLOCK );
-        case EPERM:
-        case EACCES :
-            return( AFPERR_ACCESS );
-        case EEXIST :
-            return( AFPERR_EXIST );
-        case ENOSPC :
-        case EDQUOT :
-            return( AFPERR_DFULL );
-        default :
-            return( AFPERR_PARAM );
-        }
-    }
-    return AFP_OK;
-}
-
-/* ------------------- */
-static int deletedir(int dirfd, char *dir)
-{
-    char path[MAXPATHLEN + 1];
-    DIR *dp;
-    struct dirent   *de;
-    struct stat st;
-    size_t len;
-    int err = AFP_OK;
-    size_t remain;
-
-    if ((len = strlen(dir)) +2 > sizeof(path))
-        return AFPERR_PARAM;
-
-    /* already gone */
-    if ((dp = opendirat(dirfd, dir)) == NULL)
-        return AFP_OK;
-
-    strcpy(path, dir);
-    strcat(path, "/");
-    len++;
-    remain = sizeof(path) -len -1;
-    while ((de = readdir(dp)) && err == AFP_OK) {
-        /* skip this and previous directory */
-        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
-            continue;
-
-        if (strlen(de->d_name) > remain) {
-            err = AFPERR_PARAM;
-            break;
-        }
-        strcpy(path + len, de->d_name);
-        if (lstatat(dirfd, path, &st)) {
-            continue;
-        }
-        if (S_ISDIR(st.st_mode)) {
-            err = deletedir(dirfd, path);
-        } else {
-            err = netatalk_unlinkat(dirfd, path);
-        }
-    }
-    closedir(dp);
-
-    /* okay. the directory is empty. delete it. note: we already got rid
-       of .AppleDouble.  */
-    if (err == AFP_OK) {
-        err = netatalk_rmdir(dirfd, dir);
-    }
-    return err;
-}
-
-/* do a recursive copy. */
-static int copydir(const struct vol *vol, int dirfd, char *src, char *dst)
-{
-    char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
-    DIR *dp;
-    struct dirent   *de;
-    struct stat st;
-    struct utimbuf      ut;
-    size_t slen, dlen;
-    size_t srem, drem;
-    int err;
-
-    /* doesn't exist or the path is too long. */
-    if (((slen = strlen(src)) > sizeof(spath) - 2) ||
-        ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
-        ((dp = opendirat(dirfd, src)) == NULL))
-        return AFPERR_PARAM;
-
-    /* try to create the destination directory */
-    if (AFP_OK != (err = netatalk_mkdir(vol, dst)) ) {
-        closedir(dp);
-        return err;
+    if (convert_string_allocate( (utf8_encoding()) ? CH_UTF8_MAC : vol->v_maccharset,
+                                 CH_UCS2,
+                                 m_name,
+                                 -1, (char **)&dir->d_m_name_ucs2) == (size_t)-1 ) {
+        LOG(log_error, logtype_afpd, "dir_new(did: %u) {%s, %s}: couldn't set UCS2 name", ntohl(did), m_name, u_name);
+        dir->d_m_name_ucs2 = NULL;
     }
 
-    /* set things up to copy */
-    strcpy(spath, src);
-    strcat(spath, "/");
-    slen++;
-    srem = sizeof(spath) - slen -1;
-
-    strcpy(dpath, dst);
-    strcat(dpath, "/");
-    dlen++;
-    drem = sizeof(dpath) - dlen -1;
-
-    err = AFP_OK;
-    while ((de = readdir(dp))) {
-        /* skip this and previous directory */
-        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
-            continue;
-
-        if (strlen(de->d_name) > srem) {
-            err = AFPERR_PARAM;
-            break;
-        }
-        strcpy(spath + slen, de->d_name);
-
-        if (lstatat(dirfd, spath, &st) == 0) {
-            if (strlen(de->d_name) > drem) {
-                err = AFPERR_PARAM;
-                break;
-            }
-            strcpy(dpath + dlen, de->d_name);
-
-            if (S_ISDIR(st.st_mode)) {
-                if (AFP_OK != (err = copydir(vol, dirfd, spath, dpath)))
-                    goto copydir_done;
-            } else if (AFP_OK != (err = copyfile(vol, vol, dirfd, spath, dpath, NULL, NULL))) {
-                goto copydir_done;
-
-            } else {
-                /* keep the same time stamp. */
-                ut.actime = ut.modtime = st.st_mtime;
-                utime(dpath, &ut);
-            }
-        }
+    if (m_name == u_name || !strcmp(m_name, u_name)) {
+        dir->d_u_name = dir->d_m_name;
     }
-
-    /* keep the same time stamp. */
-    if (lstatat(dirfd, src, &st) == 0) {
-        ut.actime = ut.modtime = st.st_mtime;
-        utime(dst, &ut);
+    else if ((dir->d_u_name = bfromcstr(u_name)) == NULL) {
+        bdestroy(dir->d_m_name);
+        free(dir);
+        return NULL;
     }
 
-copydir_done:
-    closedir(dp);
-    return err;
+    dir->d_did = did;
+    dir->d_pdid = pdid;
+    dir->d_vid = vol->v_vid;
+    dir->d_fullpath = path;
+    dir->ctime_dircache = ctime;
+    return dir;
 }
 
-
-/* --- public functions follow --- */
-
-/* NOTE: we start off with at least one node (the root directory). */
-static struct dir *dirinsert(struct vol *vol, struct dir *dir)
+/*!
+ * @brief Free a struct dir and all its members
+ *
+ * @param (rw) pointer to struct dir
+ */
+void dir_free(struct dir *dir)
 {
-    struct dir *node;
-
-    if ((node = dir_insert(vol, dir)))
-        return node;
-
-    /* recolor the tree. the current node is red. */
-    dir->d_color = DIRTREE_COLOR_RED;
-
-    /* parent of this node has to be black. if the parent node
-     * is red, then we have a grandparent. */
-    while ((dir != vol->v_root) &&
-           (dir->d_back->d_color == DIRTREE_COLOR_RED)) {
-        /* are we on the left tree? */
-        if (dir->d_back == dir->d_back->d_back->d_left) {
-            node = dir->d_back->d_back->d_right;  /* get the right node */
-            if (node->d_color == DIRTREE_COLOR_RED) {
-                /* we're red. we need to change to black. */
-                dir->d_back->d_color = DIRTREE_COLOR_BLACK;
-                node->d_color = DIRTREE_COLOR_BLACK;
-                dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
-                dir = dir->d_back->d_back; /* finished. go up. */
-            } else {
-                if (dir == dir->d_back->d_right) {
-                    dir = dir->d_back;
-                    dir_leftrotate(vol, dir);
-                }
-                dir->d_back->d_color = DIRTREE_COLOR_BLACK;
-                dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
-                dir_rightrotate(vol, dir->d_back->d_back);
-            }
-        } else {
-            node = dir->d_back->d_back->d_left;
-            if (node->d_color == DIRTREE_COLOR_RED) {
-                /* we're red. we need to change to black. */
-                dir->d_back->d_color = DIRTREE_COLOR_BLACK;
-                node->d_color = DIRTREE_COLOR_BLACK;
-                dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
-                dir = dir->d_back->d_back; /* finished. ascend */
-            } else {
-                if (dir == dir->d_back->d_left) {
-                    dir = dir->d_back;
-                    dir_rightrotate(vol, dir);
-                }
-                dir->d_back->d_color = DIRTREE_COLOR_BLACK;
-                dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
-                dir_leftrotate(vol, dir->d_back->d_back);
-            }
-        }
+    if (dir->d_u_name != dir->d_m_name) {
+        bdestroy(dir->d_u_name);
     }
-
-    vol->v_root->d_color = DIRTREE_COLOR_BLACK;
-    return NULL;
+    if (dir->d_m_name_ucs2)
+        free(dir->d_m_name_ucs2);
+    bdestroy(dir->d_m_name);
+    bdestroy(dir->d_fullpath);
+    free(dir);
 }
 
-/* ---------------------------- */
-struct dir *
-adddir(struct vol *vol, struct dir *dir, struct path *path)
+/*!
+ * @brief Create struct dir from struct path
+ *
+ * Create a new struct dir from struct path. Then add it to the cache.
+ *
+ * 1. Open adouble file, get CNID from it.
+ * 2. Search the database, hinting with the CNID from (1).
+ * 3. Build fullpath and create struct dir.
+ * 4. Add it to the cache.
+ *
+ * @param vol   (r) pointer to struct vol, possibly modified in callee
+ * @param dir   (r) pointer to parrent directory
+ * @param path  (rw) pointer to struct path with valid path->u_name
+ * @param len   (r) strlen of path->u_name
+ *
+ * @returns Pointer to new struct dir or NULL on error.
+ *
+ * @note Function also assigns path->m_name from path->u_name.
+ */
+struct dir *dir_add(struct vol *vol, const struct dir *dir, struct path *path, int len)
 {
-    struct dir  *cdir, *edir;
-    int     upathlen;
-    char        *name;
-    char        *upath;
-    struct stat *st;
-    int         deleted;
+    int err = 0;
+    struct dir  *cdir = NULL;
+    cnid_t      id;
     struct adouble  ad;
     struct adouble *adp = NULL;
-    cnid_t      id;
-
-    upath = path->u_name;
-    st    = &path->st;
-    upathlen = strlen(upath);
+    bstring fullpath;
+
+    AFP_ASSERT(vol);
+    AFP_ASSERT(dir);
+    AFP_ASSERT(path);
+    AFP_ASSERT(len > 0);
+
+    if ((cdir = dircache_search_by_name(vol, dir, path->u_name, strlen(path->u_name), path->st.st_ctime)) != NULL) {
+        /* there's a stray entry in the dircache */
+        LOG(log_debug, logtype_afpd, "dir_add(did:%u,'%s/%s'): {stray cache entry: did:%u,'%s', removing}",
+            ntohl(dir->d_did), cfrombstr(dir->d_fullpath), path->u_name,
+            ntohl(cdir->d_did), cfrombstr(dir->d_fullpath));
+        if (dir_remove(vol, cdir) != 0) {
+            dircache_dump();
+            AFP_PANIC("dir_add");
+        }
+    }
 
     /* get_id needs adp for reading CNID from adouble file */
     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
-    if ((ad_open_metadata(upath, ADFLAGS_DIR, 0, &ad)) == 0)
+    if ((ad_open_metadata(path->u_name, ADFLAGS_DIR, 0, &ad)) == 0) /* 1 */
         adp = &ad;
 
-    id = get_id(vol, adp, st, dir->d_did, upath, upathlen);
+    /* Get CNID */
+    if ((id = get_id(vol, adp, &path->st, dir->d_did, path->u_name, len)) == 0) { /* 2 */
+        err = 1;
+        goto exit;
+    }
 
     if (adp)
         ad_close_metadata(adp);
 
-    if (id == 0) {
-        return NULL;
-    }
-    if (!path->m_name && !(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
-        return NULL;
-    }
-    name  = path->m_name;
-    if ((cdir = dirnew(name, upath)) == NULL) {
-        LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
-        return NULL;
-    }
-    if ((size_t)-1 == convert_string_allocate((utf8_encoding())?CH_UTF8_MAC:vol->v_maccharset, CH_UCS2, path->m_name, -1, (char **)&cdir->d_m_name_ucs2)) {
-        LOG(log_error, logtype_afpd, "Couldn't set UCS2 name for %s", name);
-        cdir->d_m_name_ucs2 = NULL;
-    }
-
-    cdir->d_did = id;
-
-    if ((edir = dirinsert( vol, cdir ))) {
-        /* it's not possible with LASTDID
-           for CNID:
-           - someone else have moved the directory.
-           - it's a symlink inside the share.
-           - it's an ID reused, the old directory was deleted but not
-           the cnid record and the server've reused the inode for
-           the new dir.
-           for HASH (we should get ride of HASH)
-           - someone else have moved the directory.
-           - it's an ID reused as above
-           - it's a hash duplicate and we are in big trouble
-        */
-        deleted = (edir->d_m_name == NULL);
-        if (!deleted)
-            dir_hash_del(vol, edir);
-        dirfreename(edir);
-        edir->d_m_name = cdir->d_m_name;
-        edir->d_u_name = cdir->d_u_name;
-        edir->d_m_name_ucs2 = cdir->d_m_name_ucs2;
-        free(cdir);
-        cdir = edir;
-        LOG(log_error, logtype_afpd, "adddir: insert %s", edir->d_m_name);
-        if (!cdir->d_parent || (cdir->d_parent == dir && !deleted)) {
-            hash_alloc_insert(vol->v_hash, cdir, cdir);
-            return cdir;
+    /* Get macname from unixname */
+    if (path->m_name == NULL) {
+        if ((path->m_name = utompath(vol, path->u_name, id, utf8_encoding())) == NULL) {
+            LOG(log_error, logtype_afpd, "dir_add(\"%s\"): can't assign macname", path->u_name);
+            err = 2;
+            goto exit;
         }
-        /* the old was not in the same folder */
-        if (!deleted)
-            dirchildremove(cdir->d_parent, cdir);
-    }
-
-    /* parent/child directories */
-    cdir->d_parent = dir;
-    dirchildadd(vol, dir, cdir);
-    return( cdir );
-}
-
-/* --- public functions follow --- */
-/* free everything down. we don't bother to recolor as this is only
- * called to free the entire tree */
-void dirfreename(struct dir *dir)
-{
-    if (dir->d_u_name != dir->d_m_name) {
-        free(dir->d_u_name);
-    }
-    if (dir->d_m_name_ucs2)
-        free(dir->d_m_name_ucs2);
-    free(dir->d_m_name);
-}
-
-void dirfree(struct dir *dir)
-{
-    if (!dir || (dir == SENTINEL))
-        return;
-
-    if ( dir->d_left != SENTINEL ) {
-        dirfree( dir->d_left );
-    }
-    if ( dir->d_right != SENTINEL ) {
-        dirfree( dir->d_right );
-    }
-
-    if (dir != SENTINEL) {
-        dirfreename(dir);
-        free( dir );
-    }
-}
-
-/* --------------------------------------------
- * most of the time mac name and unix name are the same
- */
-struct dir *dirnew(const char *m_name, const char *u_name)
-{
-    struct dir *dir;
-
-    dir = (struct dir *) calloc(1, sizeof( struct dir ));
-    if (!dir)
-        return NULL;
+    }
 
-    if ((dir->d_m_name = strdup(m_name)) == NULL) {
-        free(dir);
-        return NULL;
+    /* Build fullpath */
+    if ( ((fullpath = bstrcpy(dir->d_fullpath)) == NULL) /* 3 */
+         || (bconchar(fullpath, '/') != BSTR_OK)
+         || (bcatcstr(fullpath, path->u_name)) != BSTR_OK) {
+        LOG(log_error, logtype_afpd, "dir_add: fullpath: %s", strerror(errno) );
+        err = 3;
+        goto exit;
     }
 
-    if (m_name == u_name || !strcmp(m_name, u_name)) {
-        dir->d_u_name = dir->d_m_name;
+    /* Allocate and initialize struct dir */
+    if ((cdir = dir_new( path->m_name, path->u_name, vol, dir->d_did, id, fullpath, path->st.st_ctime)) == NULL) { /* 3 */
+        err = 4;
+        goto exit;
     }
-    else if ((dir->d_u_name = strdup(u_name)) == NULL) {
-        free(dir->d_m_name);
-        free(dir);
-        return NULL;
+
+    if ((dircache_add(vol, cdir)) != 0) { /* 4 */
+        LOG(log_error, logtype_afpd, "dir_add: fatal dircache error: %s", cfrombstr(fullpath));
+        exit(EXITERR_SYS);
     }
 
-    dir->d_m_name_ucs2 = NULL;
-    dir->d_left = dir->d_right = SENTINEL;
-    dir->d_next = dir->d_prev = dir;
-    return dir;
+exit:
+    if (err != 0) {
+        LOG(log_debug, logtype_afpd, "dir_add('%s/%s'): error: %u",
+            cfrombstr(dir->d_u_name), path->u_name, err);
+
+        if (adp)
+            ad_close_metadata(adp);
+        if (!cdir && fullpath)
+            bdestroy(fullpath);
+        if (cdir)
+            dir_free(cdir);
+        cdir = NULL;
+    } else {
+        /* no error */
+        LOG(log_debug, logtype_afpd, "dir_add(did:%u,'%s/%s'): {cached: %u,'%s'}",
+            ntohl(dir->d_did), cfrombstr(dir->d_fullpath), path->u_name,
+            ntohl(cdir->d_did), cfrombstr(cdir->d_fullpath));
+    }
+
+    return(cdir);
 }
 
-#if 0
-/* ------------------ */
-static hash_val_t hash_fun_dir(const void *key)
+/*!
+ * Free the queue with invalid struct dirs
+ *
+ * This gets called at the end of every AFP func.
+ */
+void dir_free_invalid_q(void)
 {
-    const struct dir *k = key;
-
-    static unsigned long randbox[] = {
-        0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
-        0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
-        0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
-        0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
-    };
-
-    const unsigned char *str = (unsigned char *)(k->d_u_name);
-    hash_val_t acc = k->d_parent->d_did;
-
-    while (*str) {
-        acc ^= randbox[(*str + acc) & 0xf];
-        acc = (acc << 1) | (acc >> 31);
-        acc &= 0xffffffffU;
-        acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
-        acc = (acc << 2) | (acc >> 30);
-        acc &= 0xffffffffU;
-    }
-    return acc;
+    struct dir *dir;
+    while (dir = (struct dir *)dequeue(invalid_dircache_entries))
+        dir_free(dir);
 }
-#endif
 
-#undef get16bits
-#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__)    \
-    || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
-#define get16bits(d) (*((const uint16_t *) (d)))
-#endif
+/*!
+ * @brief Remove a dir from a cache and queue it for freeing
+ *
+ * 1. Check if the dir is locked or has opened forks
+ * 2. Remove it from the cache
+ * 3. Queue it for removal
+ * 4. If it's a request to remove curdir, mark curdir as invalid
+ * 5. Mark it as invalid
+ *
+ * @param (r) pointer to struct vol
+ * @param (rw) pointer to struct dir
+ */
+int dir_remove(const struct vol *vol, struct dir *dir)
+{
+    AFP_ASSERT(vol);
+    AFP_ASSERT(dir);
 
-#if !defined (get16bits)
-#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)    \
-                      +(uint32_t)(((const uint8_t *)(d))[0]) )
-#endif
+    if (dir->d_did == DIRDID_ROOT_PARENT || dir->d_did == DIRDID_ROOT)
+        return 0;
 
-static hash_val_t hash_fun2_dir(const void *key)
-{
-    const struct dir *k = key;
-    const char *data = k->d_u_name;
-    int len = strlen(k->d_u_name);
-    hash_val_t hash = k->d_parent->d_did, tmp;
-
-    int rem = len & 3;
-    len >>= 2;
-
-    /* Main loop */
-    for (;len > 0; len--) {
-        hash  += get16bits (data);
-        tmp    = (get16bits (data+2) << 11) ^ hash;
-        hash   = (hash << 16) ^ tmp;
-        data  += 2*sizeof (uint16_t);
-        hash  += hash >> 11;
-    }
-
-    /* Handle end cases */
-    switch (rem) {
-    case 3: hash += get16bits (data);
-        hash ^= hash << 16;
-        hash ^= data[sizeof (uint16_t)] << 18;
-        hash += hash >> 11;
-        break;
-    case 2: hash += get16bits (data);
-        hash ^= hash << 11;
-        hash += hash >> 17;
-        break;
-    case 1: hash += *data;
-        hash ^= hash << 10;
-        hash += hash >> 1;
-    }
+    LOG(log_debug, logtype_afpd, "dir_remove(did:%u,'%s'): {removing}",
+        ntohl(dir->d_did), cfrombstr(dir->d_u_name));
 
-    /* Force "avalanching" of final 127 bits */
-    hash ^= hash << 3;
-    hash += hash >> 5;
-    hash ^= hash << 4;
-    hash += hash >> 17;
-    hash ^= hash << 25;
-    hash += hash >> 6;
+    dircache_remove(vol, dir, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX); /* 2 */
+    enqueue(invalid_dircache_entries, dir); /* 3 */
 
-    return hash;
-}
+    if (curdir == dir)                      /* 4 */
+        curdir = NULL;
 
-/* ---------------- */
-static int hash_comp_dir(const void *key1, const void *key2)
-{
-    const struct dir *k1 = key1;
-    const struct dir *k2 = key2;
+    dir->d_did = CNID_INVALID;              /* 5 */
 
-    return !(k1->d_parent->d_did == k2->d_parent->d_did && !strcmp(k1->d_u_name, k2->d_u_name));
+    return 0;
 }
 
-/* ---------------- */
-hash_t *
-dirhash(void)
+#if 0 /* unused */
+/*!
+ * @brief Modify a struct dir, adjust cache
+ *
+ * Any value that is 0 or NULL is not changed. If new_uname is NULL it is set to new_mname.
+ * If given new_uname == new_mname, new_uname will point to new_mname.
+ *
+ * @param vol       (r) pointer to struct vol
+ * @param dir       (rw) pointer to struct dir
+ * @param pdid      (r) new parent DID
+ * @param did       (r) new DID
+ * @param new_mname (r) new mac-name
+ * @param new_uname (r) new unix-name
+ * @param pdir_fullpath (r) new fullpath of parent dir
+ */
+int dir_modify(const struct vol *vol,
+               struct dir *dir,
+               cnid_t pdid,
+               cnid_t did,
+               const char *new_mname,
+               const char *new_uname,
+               bstring pdir_fullpath)
 {
-    return hash_create(HASHCOUNT_T_MAX, hash_comp_dir, hash_fun2_dir);
-}
+    int ret = 0;
 
-/* ------------------ */
-static struct path *invalidate (struct vol *vol, struct dir *dir, struct path *ret)
-{
-    /* it's tricky:
-       movecwd failed some of dir path are not there anymore.
-       FIXME Is it true with other errors?
-       so we remove dir from the cache
-    */
-    if (dir->d_did == DIRDID_ROOT_PARENT)
-        return NULL;
-    if (afp_errno == AFPERR_ACCESS) {
-        if ( movecwd( vol, dir->d_parent ) < 0 ) {
-            return NULL;
-        }
-        /* FIXME should we set these?, don't need to call stat() after:
-           ret->st_valid = 1;
-           ret->st_errno = EACCES;
-        */
-        ret->m_name = dir->d_m_name;
-        ret->u_name = dir->d_u_name;
-        ret->d_dir = dir;
-        return ret;
-    } else if (afp_errno == AFPERR_NOOBJ) {
-        if ( movecwd( vol, dir->d_parent ) < 0 ) {
-            return NULL;
-        }
-        strcpy(ret->m_name, dir->d_m_name);
-        if (dir->d_m_name == dir->d_u_name) {
-            ret->u_name = ret->m_name;
+    /* Remove it from the cache */
+    dircache_remove(vol, dir, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX);
+
+    if (pdid)
+        dir->d_pdid = pdid;
+    if (did)
+        dir->d_did = did;
+
+    if (new_mname) {
+        /* free uname if it's not the same as mname */
+        if (dir->d_m_name != dir->d_u_name)
+            bdestroy(dir->d_u_name);
+
+        if (new_uname == NULL)
+            new_uname = new_mname;
+
+        /* assign new name */
+        if ((bassigncstr(dir->d_m_name, new_mname)) != BSTR_OK) {
+            LOG(log_error, logtype_afpd, "dir_modify: bassigncstr: %s", strerror(errno) );
+            return -1;
         }
-        else {
-            size_t tp = strlen(ret->m_name)+1;
 
-            ret->u_name =  ret->m_name +tp;
-            strcpy(ret->u_name, dir->d_u_name);
+        if (new_mname == new_uname || (strcmp(new_mname, new_uname) == 0)) {
+            dir->d_u_name = dir->d_m_name;
+        } else {
+            if ((dir->d_u_name = bfromcstr(new_uname)) == NULL) {
+                LOG(log_error, logtype_afpd, "dir_modify: bassigncstr: %s", strerror(errno) );
+                return -1;
+            }
         }
-        /* FIXME should we set :
-           ret->st_valid = 1;
-           ret->st_errno = ENOENT;
-        */
-        dir_invalidate(vol, dir);
-        return ret;
     }
-    dir_invalidate(vol, dir);
-    return NULL;
-}
 
-/* -------------------------------------------------- */
-/* cname
-   return
-   if it's a filename:
-   in extenddir:
-   compute unix name
-   stat the file or errno
-   return
-   filename
-   curdir: filename parent directory
-
-   if it's a dirname:
-   not in the cache
-   in extenddir
-   compute unix name
-   stat the dir or errno
-   return
-   if chdir error
-   dirname
-   curdir: dir parent directory
-   sinon
-   dirname: ""
-   curdir: dir
-   in the cache
-   return
-   if chdir error
-   dirname
-   curdir: dir parent directory
-   else
-   dirname: ""
-   curdir: dir
+    if (pdir_fullpath) {
+        if (bassign(dir->d_fullpath, pdir_fullpath) != BSTR_OK)
+            return -1;
+        if (bcatcstr(dir->d_fullpath, "/") != BSTR_OK)
+            return -1;
+        if (bcatcstr(dir->d_fullpath, new_uname) != BSTR_OK)
+            return -1;
+    }
 
-*/
-struct path *
-cname(struct vol *vol, struct dir *dir, char **cpath)
+    if (dir->d_m_name_ucs2)
+        free(dir->d_m_name_ucs2);
+    if ((size_t)-1 == convert_string_allocate((utf8_encoding())?CH_UTF8_MAC:vol->v_maccharset, CH_UCS2, dir->d_m_name, -1, (char**)&dir->d_m_name_ucs2))
+        dir->d_m_name_ucs2 = NULL;
+
+    /* Re-add it to the cache */
+    if ((dircache_add(vol, dir)) != 0) {
+        dircache_dump();
+        AFP_PANIC("dir_modify");
+    }
+
+    return ret;
+}
+#endif
+
+/*!
+ * @brief Resolve a catalog node name path
+ *
+ * 1. Evaluate path type
+ * 2. Move to start dir, if we cant, it might eg because of EACCES, build
+ *    path from dirname, so eg getdirparams has sth it can chew on. curdir
+ *    is dir parent then. All this is done in path_from_dir().
+ * 3. Parse next cnode name in path, cases:
+ * 4.   single "\0" -> do nothing
+ * 5.   two or more consecutive "\0" -> chdir("..") one or more times
+ * 6.   cnode name -> copy it to path.m_name
+ * 7. Get unix name from mac name
+ * 8. Special handling of request with did 1
+ * 9. stat the cnode name
+ * 10. If it's not there, it's probably an afp_createfile|dir,
+ *     return with curdir = dir parent, struct path = dirname
+ * 11. If it's there and it's a file, it must should be the last element of the requested
+ *     path. Return with curdir = cnode name parent dir, struct path = filename
+ * 12. Treat symlinks like files, dont follow them
+ * 13. If it's a dir:
+ * 14. Search the dircache for it
+ * 15. If it's not in the cache, create a struct dir for it and add it to the cache
+ * 16. chdir into the dir and
+ * 17. set m_name to the mac equivalent of "."
+ * 18. goto 3
+ */
+struct path *cname(struct vol *vol, struct dir *dir, char **cpath)
 {
-    struct dir         *cdir, *scdir=NULL;
     static char        path[ MAXPATHLEN + 1];
     static struct path ret;
 
+    struct dir  *cdir;
     char        *data, *p;
-    int         extend = 0;
     int         len;
     u_int32_t   hint;
     u_int16_t   len16;
     int         size = 0;
-    char        sep;
     int         toUTF8 = 0;
 
+    LOG(log_maxdebug, logtype_afpd, "came('%s'): {start}", cfrombstr(dir->d_fullpath));
+
     data = *cpath;
     afp_errno = AFPERR_NOOBJ;
     memset(&ret, 0, sizeof(ret));
-    switch (ret.m_type = *data) { /* path type */
+
+    switch (ret.m_type = *data) { /* 1 */
     case 2:
         data++;
         len = (unsigned char) *data++;
         size = 2;
-        sep = 0;
         if (afp_version >= 30) {
             ret.m_type = 3;
             toUTF8 = 1;
@@ -1360,7 +1149,6 @@ cname(struct vol *vol, struct dir *dir, char **cpath)
             len = ntohs(len16);
             data += 2;
             size = 7;
-            sep = 0; /* '/';*/
             break;
         }
         /* else it's an error */
@@ -1369,248 +1157,215 @@ cname(struct vol *vol, struct dir *dir, char **cpath)
         return( NULL );
     }
     *cpath += len + size;
-    *path = '\0';
+
+    path[0] = 0;
     ret.m_name = path;
-    for ( ;; ) {
-        if ( len == 0 ) {
-            if (movecwd( vol, dir ) < 0 ) {
-                return invalidate(vol, dir, &ret );
-            }
-            if (*path == '\0') {
-                ret.u_name = ".";
-                ret.d_dir = dir;
-            }
-            return &ret;
-        }
 
-        if (*data == sep ) {
+    if (movecwd(vol, dir) < 0 ) {
+        LOG(log_debug, logtype_afpd, "cname(did:%u): failed to chdir to '%s'",
+            ntohl(dir->d_did), cfrombstr(dir->d_fullpath));
+        if (len == 0)
+            return path_from_dir(vol, dir, &ret);
+        else
+            return NULL;
+    }
+
+    while (len) {         /* 3 */
+        if (*data == 0) { /* 4 or 5 */
             data++;
             len--;
-        }
-        while (*data == sep && len > 0 ) {
-            if ( dir->d_parent == NULL ) {
-                return NULL;
+            while (len > 0 && *data == 0) { /* 5 */
+                /* chdir to parrent dir */
+                if ((dir = dirlookup(vol, dir->d_pdid)) == NULL)
+                    return NULL;
+                if (movecwd( vol, dir ) < 0 ) {
+                    dir_remove(vol, dir);
+                    return NULL;
+                }
+                data++;
+                len--;
             }
-            dir = dir->d_parent;
-            data++;
-            len--;
+            continue;
         }
 
-        /* would this be faster with strlen + strncpy? */
-        p = path;
-        while ( *data != sep && len > 0 ) {
+        /* 6*/
+        for ( p = path; *data != 0 && len > 0; len-- ) {
             *p++ = *data++;
-            if (p > &path[ MAXPATHLEN]) {
+            if (p > &path[UTF8FILELEN_EARLY]) {   /* FIXME safeguard, limit of early Mac OS X */
                 afp_errno = AFPERR_PARAM;
-                return( NULL );
+                return NULL;
             }
-            len--;
         }
+        *p = 0;            /* Terminate string */
+        ret.u_name = NULL;
 
-        /* short cut bits by chopping off a trailing \0. this also
-           makes the traversal happy w/ filenames at the end of the
-           cname. */
-        if (len == 1)
-            len--;
-
-        *p = '\0';
-
-        if ( p == path ) { /* end of the name parameter */
-            continue;
+        if (cname_mtouname(vol, dir, &ret, toUTF8) != 0) { /* 7 */
+            LOG(log_error, logtype_afpd, "cname('%s'): error from cname_mtouname", path);
+            return NULL;
         }
-        ret.u_name = NULL;
-        if (afp_version >= 30) {
-            char *t;
-            cnid_t fileid;
 
-            if (toUTF8) {
-                static char temp[ MAXPATHLEN + 1];
+        LOG(log_maxdebug, logtype_afpd, "came('%s'): {node: '%s}", cfrombstr(dir->d_fullpath), ret.u_name);
 
-                if (dir->d_did == DIRDID_ROOT_PARENT) {
-                    /*
-                      With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
-                      So we compare it with the longname from the current volume and if they match
-                      we overwrite the requested path with the utf8 volume name so that the following
-                      strcmp can match.
-                    */
-                    ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
-                    if (strcasecmp( path, temp) == 0)
-                        ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, path, AFPVOL_U8MNAMELEN);
-                } else {
-                    /* toUTF8 */
-                    if (mtoUTF8(vol, path, strlen(path), temp, MAXPATHLEN) == (size_t)-1) {
-                        afp_errno = AFPERR_PARAM;
-                        return( NULL );
-                    }
-                    strcpy(path, temp);
-                }
-            }
-            /* check for OS X mangled filename :( */
+        /* Prevent access to our special folders like .AppleDouble */
+        if (check_name(vol, ret.u_name)) {
+            /* the name is illegal */
+            LOG(log_info, logtype_afpd, "cname: illegal path: '%s'", ret.u_name);
+            afp_errno = AFPERR_PARAM;
+            return NULL;
+        }
 
-            t = demangle_osx(vol, path, dir->d_did, &fileid);
-            if (t != path) {
-                ret.u_name = t;
-                /* duplicate work but we can't reuse all convert_char we did in demangle_osx
-                 * flags weren't the same
+        if (dir->d_did == DIRDID_ROOT_PARENT) { /* 8 */
+            /*
+             * Special case: CNID 1
+             * root parent (did 1) has one child: the volume. Requests for did=1 with
+             * some <name> must check against the volume name.
+             */
+            if ((strcmp(cfrombstr(vol->v_root->d_m_name), ret.m_name)) == 0)
+                cdir = vol->v_root;
+            else
+                return NULL;
+        } else {
+            /*
+             * CNID != 1, eg. most of the times we take this way.
+             * Now check if current path-part is a file or dir:
+             * o if it's dir we have to step into it
+             * o if it's a file we expect it to be the last part of the requested path
+             *   and thus call continue which should terminate the while loop because
+             *   len = 0. Ok?
+             */
+            if (of_stat(&ret) != 0) { /* 9 */
+                /*
+                 * ret.u_name doesn't exist, might be afp_createfile|dir
+                 * that means it should have been the last part
                  */
-                if ( (t = utompath(vol, ret.u_name, fileid, utf8_encoding())) ) {
-                    /* at last got our view of mac name */
-                    strcpy(path,t);
+                if (len > 0) {
+                    /* it wasn't the last part, so we have a bogus path request */
+                    afp_errno = AFPERR_NOOBJ;
+                    return NULL;
                 }
+                /*
+                 * this will terminate clean in while (1) because len == 0,
+                 * probably afp_createfile|dir
+                 */
+                LOG(log_maxdebug, logtype_afpd, "came('%s'): {leave-cnode ENOENT (possile create request): '%s'}",
+                    cfrombstr(dir->d_fullpath), ret.u_name);
+                continue; /* 10 */
             }
-        }
-        if (ret.u_name == NULL) {
-            if (!(ret.u_name = mtoupath(vol, ret.m_name, dir->d_did, utf8_encoding()))) {
-                afp_errno = AFPERR_PARAM;
-                return NULL;
-            }
-        }
-        if ( !extend ) {
-            ucs2_t *tmpname;
-            cdir = dir->d_child;
-            scdir = NULL;
-            if ( cdir && (vol->v_flags & AFPVOL_CASEINSEN) &&
-                 (size_t)-1 != convert_string_allocate(((ret.m_type == 3)?CH_UTF8_MAC:vol->v_maccharset),
-                                                       CH_UCS2, path, -1, (char **)&tmpname) )
-            {
-                while (cdir) {
-                    if (!cdir->d_m_name_ucs2) {
-                        LOG(log_error, logtype_afpd, "cname: no UCS2 name for %s (did %u)!!!", cdir->d_m_name, ntohl(cdir->d_did) );
-                        /* this shouldn't happen !!!! */
-                        goto noucsfallback;
-                    }
 
-                    if ( strcmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
-                        break;
-                    }
-                    if ( strcasecmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
-                        scdir = cdir;
-                    }
-                    cdir = (cdir == dir->d_child->d_prev) ? NULL :cdir->d_next;
-                }
-                free(tmpname);
-            }
-            else {
-            noucsfallback:
-                if (dir->d_did == DIRDID_ROOT_PARENT) {
-                    /*
-                      root parent (did 1) has one child: the volume. Requests for did=1 with some <name>
-                      must check against the volume name.
-                    */
-                    if (!strcmp(vol->v_dir->d_m_name, ret.m_name))
-                        cdir = vol->v_dir;
-                    else
-                        cdir = NULL;
+            switch (ret.st.st_mode & S_IFMT) {
+            case S_IFREG: /* 11 */
+                LOG(log_debug, logtype_afpd, "came('%s'): {file: '%s'}",
+                    cfrombstr(dir->d_fullpath), ret.u_name);
+                if (len > 0) {
+                    /* it wasn't the last part, so we have a bogus path request */
+                    afp_errno = AFPERR_PARAM;
+                    return NULL;
                 }
-                else {
-                    cdir = dirsearch_byname(vol, dir, ret.u_name);
+                continue; /* continues while loop */
+            case S_IFLNK: /* 12 */
+                LOG(log_debug, logtype_afpd, "came('%s'): {link: '%s'}",
+                    cfrombstr(dir->d_fullpath), ret.u_name);
+                if (len > 0) {
+                    LOG(log_warning, logtype_afpd, "came('%s'): {symlinked dir: '%s'}",
+                        cfrombstr(dir->d_fullpath), ret.u_name);
+                    afp_errno = AFPERR_PARAM;
+                    return NULL;
                 }
+                continue; /* continues while loop */
+            case S_IFDIR: /* 13 */
+                break;
+            default:
+                LOG(log_info, logtype_afpd, "cname: special file: '%s'", ret.u_name);
+                afp_errno = AFPERR_NODIR;
+                return NULL;
             }
 
-            if (cdir == NULL && scdir != NULL) {
-                cdir = scdir;
-                /* LOG(log_debug, logtype_afpd, "cname: using casediff for %s, (%s = %s)", fullpathname(cdir->d_u_name), cdir->d_m_name, path ); */
-            }
-
-            if ( cdir == NULL ) {
-                ++extend;
-                /* if dir == curdir it always succeed,
-                   even if curdir is deleted.
-                   it's not a pb because it will fail in extenddir
-                */
-                if ( movecwd( vol, dir ) < 0 ) {
-                    /* dir is not valid anymore
-                       we delete dir from the cache and abort.
-                    */
-                    if ( dir->d_did == DIRDID_ROOT_PARENT) {
-                        afp_errno = AFPERR_NOOBJ;
-                        return NULL;
-                    }
-                    if (afp_errno == AFPERR_ACCESS)
-                        return NULL;
-                    dir_invalidate(vol, dir);
+            /* Search the cache */
+            int unamelen = strlen(ret.u_name);
+            cdir = dircache_search_by_name(vol, dir, ret.u_name, unamelen, ret.st.st_ctime); /* 14 */
+            if (cdir == NULL) {
+                /* Not in cache, create one */
+                if ((cdir = dir_add(vol, dir, &ret, unamelen)) == NULL) { /* 15 */
+                    LOG(log_error, logtype_afpd, "cname(did:%u, name:'%s', cwd:'%s'): failed to add dir",
+                        ntohl(dir->d_did), ret.u_name, getcwdpath());
                     return NULL;
                 }
-                cdir = extenddir( vol, dir, &ret );
             }
+        } /* if/else cnid==1 */
+
+        /* Now chdir to the evaluated dir */
+        if (movecwd( vol, cdir ) < 0 ) { /* 16 */
+            LOG(log_debug, logtype_afpd, "cname(cwd:'%s'): failed to chdir to new subdir '%s': %s",
+                cfrombstr(curdir->d_fullpath), cfrombstr(cdir->d_fullpath), strerror(errno));
+            if (len == 0)
+                return path_from_dir(vol, cdir, &ret);
+            else
+                return NULL;
+        }
+        dir = cdir;
+        ret.m_name[0] = 0;      /* 17, so we later know last token was a dir */
+    } /* while (len) */
 
-        } else {
-            cdir = extenddir( vol, dir, &ret );
-        } /* if (!extend) */
+    if (curdir->d_did == DIRDID_ROOT_PARENT) {
+        afp_errno = AFPERR_DID1;
+        return NULL;
+    }
 
-        if ( cdir == NULL ) {
+    if (ret.m_name[0] == 0) {
+        /* Last part was a dir */
+        ret.u_name = mtoupath(vol, ret.m_name, 0, 1); /* Force "." into a useable static buffer */
+        ret.d_dir = dir;
+    }
 
-            if ( len > 0 || !ret.u_name ) {
-                return NULL;
-            }
+    LOG(log_debug, logtype_afpd, "came('%s') {end: curdir:'%s', path:'%s'}",
+        cfrombstr(dir->d_fullpath),
+        cfrombstr(curdir->d_fullpath),
+        ret.u_name);
 
-        } else {
-            dir = cdir;
-            *path = '\0';
-        }
-    } /* for (;;) */
+    return &ret;
 }
 
 /*
- * Move curdir to dir, with a possible chdir()
+ * @brief chdir() to dir
+ *
+ * @param vol   (r) pointer to struct vol
+ * @param dir   (r) pointer to struct dir
+ *
+ * @returns 0 on success, -1 on error with afp_errno set appropiately
  */
-int movecwd(struct vol *vol, struct dir *dir)
+int movecwd(const struct vol *vol, struct dir *dir)
 {
-    char path[MAXPATHLEN + 1];
-    struct dir  *d;
-    char    *p, *u;
-    int     n;
-    int     ret;
+    int ret;
 
-    if ( dir == curdir ) {
-        return( 0 );
-    }
-    if ( dir->d_did == DIRDID_ROOT_PARENT) {
-        afp_errno = AFPERR_DID1; /* AFPERR_PARAM;*/
-        return( -1 );
-    }
+    AFP_ASSERT(vol);
+    AFP_ASSERT(dir);
 
-    p = path + sizeof(path) - 1;
-    *p = '\0';
-    for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
-        u = d->d_u_name;
-        if (!u) {
-            /* parent directory is deleted */
-            afp_errno = AFPERR_NOOBJ;
-            return -1;
-        }
-        n = strlen( u );
-        if (p -n -1 < path) {
-            afp_errno = AFPERR_PARAM;
-            return -1;
-        }
-        *--p = '/';
-        p -= n;
-        memcpy( p, u, n );
-    }
-    if ( d != curdir ) {
-        n = strlen( vol->v_path );
-        if (p -n -1 < path) {
-            afp_errno = AFPERR_PARAM;
-            return -1;
-        }
-        *--p = '/';
-        p -= n;
-        memcpy( p, vol->v_path, n );
+    LOG(log_maxdebug, logtype_afpd, "movecwd: from: curdir:\"%s\", cwd:\"%s\"",
+        curdir ? cfrombstr(curdir->d_fullpath) : "INVALID", getcwdpath());
+
+    if (dir->d_did == DIRDID_ROOT_PARENT) {
+        curdir = &rootParent;
+        return 0;
     }
-    if ( (ret = lchdir(p )) != 0 ) {
-        LOG(log_debug, logtype_afpd, "movecwd('%s'): ret:%d, %u/%s", p, ret, errno, strerror(errno));
 
+    LOG(log_debug, logtype_afpd, "movecwd(to: did: %u, \"%s\")",
+        ntohl(dir->d_did), cfrombstr(dir->d_fullpath));
+
+    if ((ret = lchdir(cfrombstr(dir->d_fullpath))) != 0 ) {
+        LOG(log_debug, logtype_afpd, "movecwd(\"%s\"): ret: %u, %s",
+            cfrombstr(dir->d_fullpath), ret, strerror(errno));
         if (ret == 1) {
             /* p is a symlink or getcwd failed */
             afp_errno = AFPERR_BADTYPE;
-            vol->v_curdir = curdir = vol->v_dir;
+
             if (chdir(vol->v_path ) < 0) {
-                LOG(log_debug, logtype_afpd, "can't chdir back'%s': %s", vol->v_path, strerror(errno));
+                LOG(log_error, logtype_afpd, "can't chdir back'%s': %s", vol->v_path, strerror(errno));
                 /* XXX what do we do here? */
             }
+            curdir = vol->v_root;
             return -1;
         }
+
         switch (errno) {
         case EACCES:
         case EPERM:
@@ -1618,11 +1373,11 @@ int movecwd(struct vol *vol, struct dir *dir)
             break;
         default:
             afp_errno = AFPERR_NOOBJ;
-
         }
         return( -1 );
     }
-    vol->v_curdir = curdir = dir;
+
+    curdir = dir;
     return( 0 );
 }
 
@@ -1658,10 +1413,18 @@ int file_access(struct path *path, int mode)
     struct maccess ma;
 
     accessmode(path->u_name, &ma, curdir, &path->st);
-    if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
+
+    LOG(log_debug, logtype_afpd, "file_access(\"%s\"): mapped user mode: 0x%02x",
+        path->u_name, ma.ma_user);
+
+    if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE)) {
+        LOG(log_debug, logtype_afpd, "file_access(\"%s\"): write access denied", path->u_name);
         return -1;
-    if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
+    }
+    if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD)) {
+        LOG(log_debug, logtype_afpd, "file_access(\"%s\"): read access denied", path->u_name);
         return -1;
+    }
     return 0;
 
 }
@@ -1674,30 +1437,15 @@ void setdiroffcnt(struct dir *dir, struct stat *st,  u_int32_t count)
     dir->d_flags &= ~DIRF_CNID;
 }
 
-/* ---------------------
- * is our cached offspring count valid?
- */
-
-static int diroffcnt(struct dir *dir, struct stat *st)
-{
-    return st->st_ctime == dir->ctime;
-}
 
 /* ---------------------
  * is our cached also for reenumerate id?
  */
-
 int dirreenumerate(struct dir *dir, struct stat *st)
 {
     return st->st_ctime == dir->ctime && (dir->d_flags & DIRF_CNID);
 }
 
-/* --------------------- */
-static int invisible_dots(const struct vol *vol, const char *name)
-{
-    return vol_inv_dots(vol) && *name  == '.' && strcmp(name, ".") && strcmp(name, "..");
-}
-
 /* ------------------------------
    (".", curdir)
    (name, dir) with curdir:name == dir, from afp_enumerate
@@ -1736,20 +1484,14 @@ int getdirparams(const struct vol *vol,
                           s_path->st.st_dev,
                           s_path->st.st_ino,
                           dir->d_did,
-                          dir->d_parent->d_did,
+                          dir->d_pdid,
                           vol->v_stamp);
                 ad_flush( &ad);
             }
         }
     }
 
-    if ( dir->d_did == DIRDID_ROOT) {
-        pdid = DIRDID_ROOT_PARENT;
-    } else if (dir->d_did == DIRDID_ROOT_PARENT) {
-        pdid = 0;
-    } else {
-        pdid = dir->d_parent->d_did;
-    }
+    pdid = dir->d_pdid;
 
     data = buf;
     while ( bitmap != 0 ) {
@@ -1762,7 +1504,7 @@ int getdirparams(const struct vol *vol,
         case DIRPBIT_ATTR :
             if ( isad ) {
                 ad_getattr(&ad, &ashort);
-            } else if (invisible_dots(vol, dir->d_u_name)) {
+            } else if (invisible_dots(vol, cfrombstr(dir->d_u_name))) {
                 ashort = htons(ATTRBIT_INVISIBLE);
             } else
                 ashort = 0;
@@ -1774,6 +1516,8 @@ int getdirparams(const struct vol *vol,
         case DIRPBIT_PDID :
             memcpy( data, &pdid, sizeof( pdid ));
             data += sizeof( pdid );
+            LOG(log_debug, logtype_afpd, "metadata('%s'):     Parent DID: %u",
+                s_path->u_name, ntohl(pdid));
             break;
 
         case DIRPBIT_CDATE :
@@ -1806,7 +1550,7 @@ int getdirparams(const struct vol *vol,
                 memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
 
                 /* dot files are by default visible */
-                if (invisible_dots(vol, dir->d_u_name)) {
+                if (invisible_dots(vol, cfrombstr(dir->d_u_name))) {
                     ashort = htons(FINDERINFO_INVISIBLE);
                     memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
                 }
@@ -1830,6 +1574,8 @@ int getdirparams(const struct vol *vol,
         case DIRPBIT_DID :
             memcpy( data, &dir->d_did, sizeof( aint ));
             data += sizeof( aint );
+            LOG(log_debug, logtype_afpd, "metadata('%s'):            DID: %u",
+                s_path->u_name, ntohl(dir->d_did));
             break;
 
         case DIRPBIT_OFFCNT :
@@ -1927,12 +1673,12 @@ int getdirparams(const struct vol *vol,
     if ( l_nameoff ) {
         ashort = htons( data - buf );
         memcpy( l_nameoff, &ashort, sizeof( ashort ));
-        data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, 0);
+        data = set_name(vol, data, pdid, cfrombstr(dir->d_m_name), dir->d_did, 0);
     }
     if ( utf_nameoff ) {
         ashort = htons( data - buf );
         memcpy( utf_nameoff, &ashort, sizeof( ashort ));
-        data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, utf8);
+        data = set_name(vol, data, pdid, cfrombstr(dir->d_m_name), dir->d_did, utf8);
     }
     if ( isad ) {
         ad_close_metadata( &ad );
@@ -2017,33 +1763,7 @@ int afp_setdirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_
  *
  * assume path == '\0' eg. it's a directory in canonical form
  */
-
-struct path Cur_Path = {
-    0,
-    "",  /* mac name */
-    ".", /* unix name */
-    0,   /* id */
-    NULL,/* struct dir */
-    0,   /* stat is not set */
-};
-
-/* ------------------ */
-static int set_dir_errors(struct path *path, const char *where, int err)
-{
-    switch ( err ) {
-    case EPERM :
-    case EACCES :
-        return AFPERR_ACCESS;
-    case EROFS :
-        return AFPERR_VLOCK;
-    }
-    LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
-    return AFPERR_PARAM;
-}
-
-/* ------------------ */
-int setdirparams(struct vol *vol,
-                 struct path *path, u_int16_t d_bitmap, char *buf )
+int setdirparams(struct vol *vol, struct path *path, u_int16_t d_bitmap, char *buf )
 {
     struct maccess  ma;
     struct adouble  ad;
@@ -2195,7 +1915,7 @@ int setdirparams(struct vol *vol,
          * to set our name, etc.
          */
         if ( (ad_get_HF_flags( &ad ) & O_CREAT)) {
-            ad_setname(&ad, curdir->d_m_name);
+            ad_setname(&ad, cfrombstr(curdir->d_m_name));
         }
     }
 
@@ -2352,8 +2072,8 @@ setdirparam_done:
         if (path->st_valid && !path->st_errno) {
             struct stat *st = &path->st;
 
-            if (dir && dir->d_parent) {
-                ad_setid(&ad, st->st_dev, st->st_ino,  dir->d_did, dir->d_parent->d_did, vol->v_stamp);
+            if (dir && dir->d_pdid) {
+                ad_setid(&ad, st->st_dev, st->st_ino,  dir->d_did, dir->d_pdid, vol->v_stamp);
             }
         }
         ad_flush( &ad);
@@ -2362,7 +2082,7 @@ setdirparam_done:
 
     if (change_parent_mdate && dir->d_did != DIRDID_ROOT
         && gettimeofday(&tv, NULL) == 0) {
-        if (!movecwd(vol, dir->d_parent)) {
+        if (movecwd(vol, dirlookup(vol, dir->d_pdid)) == 0) {
             newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
             /* be careful with bitmap because now dir is null */
             bitmap = 1<<DIRPBIT_MDATE;
@@ -2457,7 +2177,7 @@ int afp_syncdir(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_,
 
         if ( fsync(dfd) < 0 )
             LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
-                vol->ad_path(dir->d_u_name, ADFLAGS_DIR), strerror(errno) );
+                vol->ad_path(cfrombstr(dir->d_u_name), ADFLAGS_DIR), strerror(errno) );
         close(dfd);
     }
 
@@ -2513,8 +2233,10 @@ int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_
     if (of_stat(s_path) < 0) {
         return AFPERR_MISC;
     }
+
     curdir->offcnt++;
-    if ((dir = adddir( vol, curdir, s_path)) == NULL) {
+
+    if ((dir = dir_add(vol, curdir, s_path, strlen(s_path->u_name))) == NULL) {
         return AFPERR_MISC;
     }
 
@@ -2537,11 +2259,6 @@ int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_
     ad_close_metadata( &ad);
 
 createdir_done:
-#ifdef HAVE_NFSv4_ACLS
-    /* FIXME: are we really inside the created dir? */
-    addir_inherit_acl(vol);
-#endif
-
     memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
     *rbuflen = sizeof( u_int32_t );
     setvoltime(obj, vol );
@@ -2563,9 +2280,7 @@ int renamedir(const struct vol *vol,
               char *newname)
 {
     struct adouble  ad;
-    struct dir      *parent;
-    char                *buf;
-    int         len, err;
+    int             err;
 
     /* existence check moved to afp_moveandrename */
     if ( unix_rename(dirfd, src, -1, dst ) < 0 ) {
@@ -2596,11 +2311,6 @@ int renamedir(const struct vol *vol,
 
     vol->vfs->vfs_renamedir(vol, dirfd, src, dst);
 
-    len = strlen( newname );
-    /* rename() succeeded so we need to update our tree even if we can't open
-     * metadata
-     */
-
     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
 
     if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) {
@@ -2609,50 +2319,6 @@ int renamedir(const struct vol *vol,
         ad_close_metadata( &ad);
     }
 
-    dir_hash_del(vol, dir);
-    if (dir->d_m_name == dir->d_u_name)
-        dir->d_u_name = NULL;
-
-    if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
-        LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
-        /* FIXME : fatal ? */
-        return AFPERR_MISC;
-    }
-    dir->d_m_name = buf;
-    strcpy( dir->d_m_name, newname );
-
-    if (newname == dst) {
-        free(dir->d_u_name);
-        dir->d_u_name = dir->d_m_name;
-    }
-    else {
-        if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
-            LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
-            return AFPERR_MISC;
-        }
-        dir->d_u_name = buf;
-        strcpy( dir->d_u_name, dst );
-    }
-
-    if (dir->d_m_name_ucs2)
-        free(dir->d_m_name_ucs2);
-
-    dir->d_m_name_ucs2 = NULL;
-    if ((size_t)-1 == convert_string_allocate((utf8_encoding())?CH_UTF8_MAC:vol->v_maccharset, CH_UCS2, dir->d_m_name, -1, (char**)&dir->d_m_name_ucs2))
-        dir->d_m_name_ucs2 = NULL;
-
-    if (( parent = dir->d_parent ) == NULL ) {
-        return( AFP_OK );
-    }
-    if ( parent == newparent ) {
-        hash_alloc_insert(vol->v_hash, dir, dir);
-        return( AFP_OK );
-    }
-
-    /* detach from old parent and add to new one. */
-    dirchildremove(parent, dir);
-    dir->d_parent = newparent;
-    dirchildadd(vol, newparent, dir);
     return( AFP_OK );
 }
 
@@ -2661,13 +2327,13 @@ int deletecurdir(struct vol *vol)
 {
     struct dirent *de;
     struct stat st;
-    struct dir  *fdir;
+    struct dir  *fdir, *pdir;
     DIR *dp;
     struct adouble  ad;
     u_int16_t       ashort;
     int err;
 
-    if ( curdir->d_parent == NULL ) {
+    if ((pdir = dirlookup(vol, curdir->d_pdid)) == NULL) {
         return( AFPERR_ACCESS );
     }
 
@@ -2685,6 +2351,8 @@ int deletecurdir(struct vol *vol)
     }
     err = vol->vfs->vfs_deletecurdir(vol);
     if (err) {
+        LOG(log_error, logtype_afpd, "deletecurdir: error deleting .AppleDouble in \"%s\"",
+            curdir->d_fullpath);
         return err;
     }
 
@@ -2697,6 +2365,8 @@ int deletecurdir(struct vol *vol)
 
             /* bail if it's not a symlink */
             if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
+                LOG(log_error, logtype_afpd, "deletecurdir(\"%s\"): not empty",
+                    curdir->d_fullpath);
                 closedir(dp);
                 return AFPERR_DIRNEMPT;
             }
@@ -2708,17 +2378,23 @@ int deletecurdir(struct vol *vol)
         }
     }
 
-    if ( movecwd( vol, curdir->d_parent ) < 0 ) {
+    if (movecwd(vol, pdir) < 0) {
         err = afp_errno;
         goto delete_done;
     }
 
-    err = netatalk_rmdir_all_errors(-1, fdir->d_u_name);
+    LOG(log_debug, logtype_afpd, "deletecurdir: moved to \"%s\"",
+        cfrombstr(curdir->d_fullpath));
+
+    err = netatalk_rmdir_all_errors(-1, cfrombstr(fdir->d_u_name));
     if ( err ==  AFP_OK || err == AFPERR_NOOBJ) {
-        dirchildremove(curdir, fdir);
         cnid_delete(vol->v_cdb, fdir->d_did);
         dir_remove( vol, fdir );
+    } else {
+        LOG(log_error, logtype_afpd, "deletecurdir(\"%s\"): netatalk_rmdir_all_errors error",
+            curdir->d_fullpath);
     }
+
 delete_done:
     if (dp) {
         /* inode is used as key for cnid.
@@ -2743,7 +2419,6 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r
     sfunc = (unsigned char) *ibuf++;
     *rbuflen = 0;
 
-
     if (sfunc >= 3 && sfunc <= 6) {
         if (afp_version < 30) {
             return( AFPERR_PARAM );
@@ -2782,17 +2457,18 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r
             name = NULL;
         }
         break;
-#ifdef HAVE_NFSv4_ACLS
+
     case 5 : /* UUID -> username */
     case 6 : /* UUID -> groupname */
         if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
             return AFPERR_PARAM;
         LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
         uuidtype_t type;
-        len = getnamefromuuid( ibuf, &name, &type);
+        len = getnamefromuuid((unsigned char*) ibuf, &name, &type);
         if (len != 0)       /* its a error code, not len */
             return AFPERR_NOITEM;
-        if (type == UUID_USER) {
+        switch (type) {
+        case UUID_USER:
             if (( pw = getpwnam( name )) == NULL )
                 return( AFPERR_NOITEM );
             LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
@@ -2803,7 +2479,8 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r
             memcpy( rbuf, &id, sizeof( id ));
             rbuf += sizeof( id );
             *rbuflen = 2 * sizeof( id );
-        } else {        /* type == UUID_GROUP */
+            break;
+        case UUID_GROUP:
             if (( gr = getgrnam( name )) == NULL )
                 return( AFPERR_NOITEM );
             LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
@@ -2814,9 +2491,15 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r
             memcpy( rbuf, &id, sizeof( id ));
             rbuf += sizeof( id );
             *rbuflen = 2 * sizeof( id );
+            break;
+        case UUID_LOCAL:
+            free(name);
+            return (AFPERR_NOITEM);
+        default:
+            return AFPERR_MISC;
         }
         break;
-#endif
+
     default :
         return( AFPERR_PARAM );
     }
@@ -2870,7 +2553,6 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz
     case 4 :
         len = (unsigned char) *ibuf++;
         break;
-#ifdef HAVE_NFSv4_ACLS
     case 5 : /* username -> UUID  */
     case 6 : /* groupname -> UUID */
         if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
@@ -2879,7 +2561,6 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz
         len = ntohs(ulen);
         ibuf += 2;
         break;
-#endif
     default :
         return( AFPERR_PARAM );
     }
@@ -2913,20 +2594,18 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz
             memcpy( rbuf, &id, sizeof( id ));
             *rbuflen = sizeof( id );
             break;
-#ifdef HAVE_NFSv4_ACLS
         case 5 :        /* username -> UUID */
             LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
-            if (0 != getuuidfromname(ibuf, UUID_USER, rbuf))
+            if (0 != getuuidfromname(ibuf, UUID_USER, (unsigned char *)rbuf))
                 return AFPERR_NOITEM;
             *rbuflen = UUID_BINSIZE;
             break;
         case 6 :        /* groupname -> UUID */
             LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
-            if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf))
+            if (0 != getuuidfromname(ibuf, UUID_GROUP, (unsigned char *)rbuf))
                 return AFPERR_NOITEM;
             *rbuflen = UUID_BINSIZE;
             break;
-#endif
         }
     }
     return( AFP_OK );
index 5334be5d5610817c7e117f2efd3390c4420f2aff..79c4a0407fe3b6c1d61bb3d2208e1cc22f319d9f 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: directory.h,v 1.34 2010-03-12 15:16:49 franklahm Exp $
- *
  * Copyright (c) 1990,1991 Regents of The University of Michigan.
  * All Rights Reserved.
  *
 #include "globals.h"
 #include "volume.h"
 
-#define DIRTREE_COLOR_RED    0
-#define DIRTREE_COLOR_BLACK  1
-
 #define DIRF_FSMASK    (3<<0)
 #define DIRF_NOFS      (0<<0)
 #define DIRF_AFS       (1<<0)
 #define DIRF_UFS       (2<<0)
 
-#define DIRF_OFFCNT     (1<<4) /* offsprings count is valid */
-#define DIRF_CNID      (1<<5)  /* renumerate id */
+#define DIRF_OFFCNT    (1<<4) /* offsprings count is valid */
+#define DIRF_CNID         (1<<5) /* renumerate id */
 
 #define AFPDIR_READ    (1<<0)
 
 #define FILDIRBIT_ISDIR        (1 << 7) /* is a directory */
 #define FILDIRBIT_ISFILE       (0)      /* is a file */
 
-/* reserved directory id's */
-#define DIRDID_ROOT_PARENT    htonl(1)  /* parent directory of root */
-#define DIRDID_ROOT           htonl(2)  /* root directory */
-
 /* file/directory ids. what a mess. we scramble things in a vain attempt
  * to get something meaningful */
 #ifndef AFS
 #define CNID(a,b)     (((a)->st_ino & 0x7fffffff) | CNID_FILE(b))
 #endif /* AFS */
 
-
 struct maccess {
     u_char     ma_user;
     u_char     ma_world;
@@ -113,46 +103,44 @@ struct maccess {
 #define        AR_UWRITE       (1<<2)
 #define        AR_UOWN         (1<<7)
 
-extern struct dir       *dirnew (const char *, const char *);
-extern void             dirfreename (struct dir *);
-extern void             dirfree (struct dir *);
-extern struct dir      *dirsearch (const struct vol *, u_int32_t);
-extern struct dir      *dirlookup (struct vol *, u_int32_t);
-extern struct dir       *dirsearch_byname (const struct vol *, struct dir *,char *);
-
-extern struct dir      *adddir (struct vol *, struct dir *, 
-                                               struct path *);
-
-extern int              movecwd (struct vol *, struct dir *);
-extern int              deletecurdir (struct vol *);
-extern struct path      *cname (struct vol *, struct dir *,
-                             char **);
-extern mode_t           mtoumode (struct maccess *);
-extern void             utommode (struct stat *, struct maccess *);
-extern int getdirparams (const struct vol *, u_int16_t, struct path *,
-                                 struct dir *, char *, size_t *);
-extern int setdirparams (struct vol *, struct path *, u_int16_t, char *);
-extern int renamedir(const struct vol *, int, char *, char *, struct dir *,
-                     struct dir *, char *);
-extern int path_error (struct path *, int error);
-
-extern void setdiroffcnt (struct dir *dir, struct stat *st,  u_int32_t count);
-extern int dirreenumerate (struct dir *dir, struct stat *st);
+q_t *invalid_dircache_entries;
 
 typedef int (*dir_loop)(struct dirent *, char *, void *);
 
-extern int  for_each_dirent (const struct vol *, char *, dir_loop , void *);
-
-extern int  check_access (char *name , int mode);
-extern int file_access   (struct path *path, int mode);
-
-extern int netatalk_unlink (const char *name);
+extern void        dir_free_invalid_q(void);
+extern struct dir  *dir_new(const char *mname, const char *uname, const struct vol *,
+                            cnid_t pdid, cnid_t did, bstring fullpath, time_t ctime);
+extern void        dir_free (struct dir *);
+extern struct dir  *dir_add(struct vol *, const struct dir *, struct path *, int);
+extern int         dir_modify(const struct vol *vol, struct dir *dir, cnid_t pdid, cnid_t did,
+                              const char *new_mname, const char *new_uname, bstring pdir_fullpath);
+extern int         dir_remove(const struct vol *vol, struct dir *dir);
+extern struct dir  *dirlookup (const struct vol *, cnid_t);
+extern struct dir *dirlookup_bypath(const struct vol *vol, const char *path);
+
+extern int         movecwd (const struct vol *, struct dir *);
+extern struct path *cname (struct vol *, struct dir *, char **);
+
+extern int         deletecurdir (struct vol *);
+extern mode_t      mtoumode (struct maccess *);
+extern void        utommode (struct stat *, struct maccess *);
+extern int         getdirparams (const struct vol *, u_int16_t, struct path *,
+                                 struct dir *, char *, size_t *);
 
-extern int caseenumerate (const struct vol *, struct path *, struct dir *);
+extern int         setdirparams(struct vol *, struct path *, u_int16_t, char *);
+extern int         renamedir(const struct vol *, int, char *, char *, struct dir *,
+                             struct dir *, char *);
+extern int         path_error(struct path *, int error);
+extern void        setdiroffcnt(struct dir *dir, struct stat *st,  u_int32_t count);
+extern int         dirreenumerate(struct dir *dir, struct stat *st);
+extern int         for_each_dirent(const struct vol *, char *, dir_loop , void *);
+extern int         check_access(char *name , int mode);
+extern int         file_access(struct path *path, int mode);
+extern int         netatalk_unlink (const char *name);
+extern int         caseenumerate (const struct vol *, struct path *, struct dir *);
 
-extern hash_t *dirhash (void);
 /* from enumerate.c */
-extern char *check_dirent (const struct vol *, char *);
+extern char        *check_dirent (const struct vol *, char *);
 
 /* FP functions */
 int afp_createdir (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf,  size_t *rbuflen);
index c06ef20300f3f296e4e2b0d41b2b40404a4538b0..1659fb77ff89cc5fbff15513fe5915600b718ae4 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: enumerate.c,v 1.49 2010-02-10 14:05:37 franklahm Exp $
- *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
  */
 #include <atalk/adouble.h>
 #include <atalk/vfs.h>
 #include <atalk/cnid.h>
+#include <atalk/util.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
+
 #include "desktop.h"
 #include "directory.h"
+#include "dircache.h"
 #include "volume.h"
 #include "globals.h"
 #include "file.h"
@@ -152,7 +155,7 @@ for_each_dirent(const struct vol *vol, char *name, dir_loop fn, void *data)
    macnamelength(1) + macname(31) + utf8(4) + utf8namelen(2) + utf8name(255) +
    oddpadding(1) */
 
-#define REPLY_PARAM_MAXLEN (4 + 104 + 1 + MACFILELEN + 4 + 2 + 255 + 1)
+#define REPLY_PARAM_MAXLEN (4 + 104 + 1 + MACFILELEN + 4 + 2 + UTF8FILELEN_EARLY + 1)
 
 /* ----------------------------- */
 static int enumerate(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, 
@@ -266,8 +269,8 @@ static int enumerate(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_,
         return path_error(o_path, AFPERR_NODIR );
     }
 
-    LOG(log_debug, logtype_afpd, "enumerate(vid:%u, did:%u, name:'%s', f/d:%04x/%04x, rc:%u, i:%u, max:%u)",
-        ntohs(vid), ntohl(did), o_path->u_name, fbitmap, dbitmap, reqcnt, sindex, maxsz);
+    LOG(log_debug, logtype_afpd, "enumerate(\"%s/%s\", f/d:%04x/%04x, rc:%u, i:%u, max:%u)",
+        getcwdpath(), o_path->u_name, fbitmap, dbitmap, reqcnt, sindex, maxsz);
 
     data = rbuf + 3 * sizeof( u_int16_t );
     sz = 3 * sizeof( u_int16_t );      /* fbitmap, dbitmap, reqcount */
@@ -281,11 +284,12 @@ static int enumerate(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_,
         sd.sd_last = sd.sd_buf;
         /* if dir was in the cache we don't have the inode */
         if (( !o_path->st_valid && lstat( ".", &o_path->st ) < 0 ) ||
-              (ret = for_each_dirent(vol, ".", enumerate_loop, (void *)&sd)) < 0) 
+            (ret = for_each_dirent(vol, ".", enumerate_loop, (void *)&sd)) < 0) 
         {
+            LOG(log_error, logtype_afpd, "enumerate: loop error: %s (%d)", strerror(errno), errno);
             switch (errno) {
             case EACCES:
-               return AFPERR_ACCESS;
+                return AFPERR_ACCESS;
             case ENOTDIR:
                 return AFPERR_BADTYPE;
             case ENOMEM:
@@ -370,19 +374,22 @@ static int enumerate(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_,
             if ( dbitmap == 0 ) {
                 continue;
             }
-            dir = dirsearch_byname(vol, curdir, s_path.u_name);
-            if (!dir && NULL == (dir = adddir( vol, curdir, &s_path) ) ) {
+            int len = strlen(s_path.u_name);
+            if ((dir = dircache_search_by_name(vol, curdir, s_path.u_name, len, s_path.st.st_ctime)) == NULL) {
+                if ((dir = dir_add(vol, curdir, &s_path, len)) == NULL) {
+                    LOG(log_error, logtype_afpd, "enumerate(vid:%u, did:%u, name:'%s'): error adding dir: '%s'",
+                        ntohs(vid), ntohl(did), o_path->u_name, s_path.u_name);
                     return AFPERR_MISC;
                 }
-            if (AFP_OK != ( ret = getdirparams(vol, dbitmap, &s_path, dir,
-                                     data + header , &esz ))) {
-                return( ret );
             }
+            if ((ret = getdirparams(vol, dbitmap, &s_path, dir, data + header , &esz)) != AFP_OK)
+                return( ret );
 
         } else {
             if ( fbitmap == 0 ) {
                 continue;
             }
+            /* files are added to the dircache in getfilparams() -> getmetadata() */
             if (AFP_OK != ( ret = getfilparams(vol, fbitmap, &s_path, curdir, 
                                      data + header , &esz )) ) {
                 return( ret );
index 4aea84dde1dcfc59bdbb56db2fd2c4ba96a44f25..8257d18b5fa115ecafea861bb11dbf855ee6bd6b 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: file.c,v 1.141 2010-03-12 15:16:49 franklahm Exp $
- *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
  */
@@ -41,6 +39,7 @@ char *strchr (), *strrchr ();
 #include <atalk/unix.h>
 
 #include "directory.h"
+#include "dircache.h"
 #include "desktop.h"
 #include "volume.h"
 #include "fork.h"
@@ -167,8 +166,8 @@ char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t
     else {
         u_int16_t temp;
 
-        if (aint > 255)  /* FIXME safeguard, anyway if no ascii char it's game over*/
-           aint = 255;
+        if (aint > UTF8FILELEN_EARLY)  /* FIXME safeguard, anyway if no ascii char it's game over*/
+           aint = UTF8FILELEN_EARLY;
 
         utf8 = vol->v_kTextEncoding;
         memcpy(data, &utf8, sizeof(utf8));
@@ -202,9 +201,27 @@ char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t
                                  (1 << FILPBIT_FNUM) |\
                                  (1 << FILPBIT_UNIXPR)))
 
-/* -------------------------- */
-u_int32_t get_id(struct vol *vol, struct adouble *adp,  const struct stat *st,
-                 const cnid_t did, char *upath, const int len) 
+/*!
+ * @brief Get CNID for did/upath args both from database and adouble file
+ *
+ * 1. Get the objects CNID as stored in its adouble file
+ * 2. Get the objects CNID from the database
+ * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
+ * 4. In case 2 and 3 differ, store 3 in the adouble file
+ *
+ * @param vol    (rw) volume
+ * @param adp    (rw) adouble struct of object upath, might be NULL
+ * @param st     (r) stat of upath, must NOT be NULL
+ * @param did    (r) parent CNID of upath
+ * @param upath  (r) name of object
+ * @param len    (r) strlen of upath
+ */
+uint32_t get_id(struct vol *vol,
+                struct adouble *adp, 
+                const struct stat *st,
+                const cnid_t did,
+                const char *upath,
+                const int len) 
 {
     static int first = 1;       /* mark if this func is called the first time */
     u_int32_t adcnid;
@@ -214,9 +231,9 @@ restart:
     if (vol->v_cdb != NULL) {
         /* prime aint with what we think is the cnid, set did to zero for
            catching moved files */
-        adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp);
+        adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
 
-           dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid);
+           dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
            /* Throw errors if cnid_add fails. */
            if (dbcnid == CNID_INVALID) {
             switch (errno) {
@@ -234,7 +251,7 @@ restart:
                 /* we have to do it here for "dbd" because it uses "lazy opening" */
                 /* In order to not end in a loop somehow with goto restart below  */
                 /*  */
-                if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) {
+                if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
                     cnid_close(vol->v_cdb);
                     free(vol->v_cnidscheme);
                     vol->v_cnidscheme = strdup("tdb");
@@ -268,9 +285,10 @@ restart:
                 goto exit;
             }
         }
-        else if (adp && (adcnid != dbcnid)) {
+        else if (adp && (adcnid != dbcnid)) { /* 4 */
             /* Update the ressource fork. For a folder adp is always null */
-            LOG(log_debug, logtype_afpd, "get_id: calling ad_setid. adcnid: %u, dbcnid: %u", htonl(adcnid), htonl(dbcnid));
+            LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
+                getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
             if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
                 ad_flush(adp);
             }
@@ -299,24 +317,52 @@ int getmetadata(struct vol *vol,
     struct stat         *st;
     struct maccess     ma;
 
-#ifdef DEBUG
-    LOG(log_debug9, logtype_afpd, "begin getmetadata:");
-#endif /* DEBUG */
+    LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
 
     upath = path->u_name;
     st = &path->st;
-
     data = buf;
 
     if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
          || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
          || (bitmap & (1 << FILPBIT_FNUM))) {
-        if (!path->id)
-            id = get_id(vol, adp, st, dir->d_did, upath, strlen(upath));
-        else 
+        if (!path->id) {
+            struct dir *cachedfile;
+            int len = strlen(upath);
+            if ((cachedfile = dircache_search_by_name(vol, dir, upath, len, st->st_ctime)) != NULL)
+                id = cachedfile->d_did;
+            else {
+                id = get_id(vol, adp, st, dir->d_did, upath, len);
+
+                /* Add it to the cache */
+                LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
+                    ntohl(dir->d_did), upath, ntohl(id));
+
+                /* Get macname from unixname first */
+                if (path->m_name == NULL) {
+                    if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
+                        LOG(log_error, logtype_afpd, "getmetadata: utompath error");
+                        exit(EXITERR_SYS);
+                    }
+                }
+                
+                if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, NULL, st->st_ctime)) == NULL) {
+                    LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
+                    exit(EXITERR_SYS);
+                }
+
+                if ((dircache_add(vol, cachedfile)) != 0) {
+                    LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
+                    exit(EXITERR_SYS);
+                }
+            }
+        } else {
             id = path->id;
+        }
+
         if (id == CNID_INVALID)
             return afp_errno;
+
         if (!path->m_name) {
             path->m_name = utompath(vol, upath, id, utf8_encoding());
         }
@@ -349,11 +395,15 @@ int getmetadata(struct vol *vol,
 #endif
             memcpy(data, &ashort, sizeof( ashort ));
             data += sizeof( ashort );
+            LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
+                path->u_name, ntohs(ashort));
             break;
 
         case FILPBIT_PDID :
             memcpy(data, &dir->d_did, sizeof( u_int32_t ));
             data += sizeof( u_int32_t );
+            LOG(log_debug, logtype_afpd, "metadata('%s'):     Parent DID: %u",
+                path->u_name, ntohl(dir->d_did));
             break;
 
         case FILPBIT_CDATE :
@@ -400,6 +450,8 @@ int getmetadata(struct vol *vol,
         case FILPBIT_FNUM :
             memcpy(data, &id, sizeof( id ));
             data += sizeof( id );
+            LOG(log_debug, logtype_afpd, "metadata('%s'):           CNID: %u",
+                path->u_name, ntohl(id));
             break;
 
         case FILPBIT_DFLEN :
@@ -564,9 +616,7 @@ int getfilparams(struct vol *vol,
     int                 opened = 0;
     int rc;    
 
-#ifdef DEBUG
-    LOG(log_debug9, logtype_default, "begin getfilparams:");
-#endif /* DEBUG */
+    LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
 
     opened = PARAM_NEED_ADP(bitmap);
     adp = NULL;
@@ -598,9 +648,6 @@ int getfilparams(struct vol *vol,
     if ( adp ) {
         ad_close_metadata( adp);
     }
-#ifdef DEBUG
-    LOG(log_debug9, logtype_afpd, "end getfilparams:");
-#endif /* DEBUG */
 
     return( rc );
 }
@@ -704,6 +751,17 @@ int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_,
 
     path = s_path->m_name;
     ad_setname(adp, path);
+
+    struct stat st;
+    if (lstat(upath, &st) != 0) {
+        LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
+            upath, strerror(errno));
+        ad_close( adp, ADFLAGS_DF|ADFLAGS_HF);
+        return AFPERR_MISC;
+    }
+
+    (void)get_id(vol, adp, &st, dir->d_did, upath, strlen(upath));
+
     ad_flush( adp);
 
     fce_register_new_file(s_path);
@@ -1058,6 +1116,9 @@ int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *n
 {
     int                rc;
 
+    LOG(log_debug, logtype_afpd,
+        "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
+
     if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
         switch ( errno ) {
         case ENOENT :
@@ -1949,6 +2010,10 @@ int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_
     }
 
     if (NULL == ( dir = dirlookup( vol, id )) ) {
+        if (afp_errno == AFPERR_NOOBJ) {
+            err = AFPERR_NOOBJ;
+            goto delete;
+        }
         return( AFPERR_PARAM );
     }
 
@@ -1972,6 +2037,7 @@ int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_
     else if (S_ISDIR(st.st_mode)) /* directories are bad */
         return AFPERR_BADTYPE;
 
+delete:
     if (cnid_delete(vol->v_cdb, fileid)) {
         switch (errno) {
         case EROFS:
@@ -2192,12 +2258,11 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
     /* id's need switching. src -> dest and dest -> src. 
      * we need to re-stat() if it was a cross device copy.
     */
-    if (sid) {
-       cnid_delete(vol->v_cdb, sid);
-    }
-    if (did) {
-       cnid_delete(vol->v_cdb, did);
-    }
+    if (sid)
+        cnid_delete(vol->v_cdb, sid);
+    if (did)
+        cnid_delete(vol->v_cdb, did);
+
     if ((did && ( (crossdev && lstat( upath, &srcst) < 0) || 
                 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
        ||
@@ -2291,5 +2356,11 @@ err_exchangefile:
        ad_close(addp, ADFLAGS_HF);
     }
 
+    struct dir *cached;
+    if ((cached = dircache_search_by_did(vol, sid)) != NULL)
+        (void)dir_remove(vol, cached);
+    if ((cached = dircache_search_by_did(vol, did)) != NULL)
+        (void)dir_remove(vol, cached);
+
     return err;
 }
index 6cf4ae22f8184d15319dddb26d6d25b75bf5c832..483f6be61a4d264fe74910a6f8c66a22a3b29f35 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: file.h,v 1.26 2010-03-12 15:16:49 franklahm Exp $
+ * $Id: file.h,v 1.26 2010/03/12 15:16:49 franklahm Exp $
  *
  * Copyright (c) 1990,1991 Regents of The University of Michigan.
  * All Rights Reserved.
@@ -127,8 +127,12 @@ extern void *get_finderinfo (const struct vol *, const char *, struct adouble *,
 extern size_t mtoUTF8   (const struct vol *, const char *, size_t , char *, size_t );
 extern int  copy_path_name (const struct vol *, char *, char *i);
 
-extern u_int32_t get_id  (struct vol *, struct adouble *, const struct stat *,
-                                const cnid_t , char *, const int );
+extern uint32_t get_id  (struct vol *,
+                         struct adouble *,
+                         const struct stat *,
+                         cnid_t ,
+                         const char *,
+                         int );
 
 /* FP functions */
 int afp_exchangefiles (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf,  size_t *rbuflen);
index 469586966ea53680094d79e180aade23f09ad828..d3976119444440fbc07a8422b4c4c12c57bf043b 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: filedir.c,v 1.73 2010-03-12 15:16:49 franklahm Exp $
- *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
  */
@@ -39,8 +37,12 @@ char *strchr (), *strrchr ();
 #include <atalk/cnid.h>
 #include <atalk/logger.h>
 #include <atalk/unix.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
+#include <atalk/acl.h>
 
 #include "directory.h"
+#include "dircache.h"
 #include "desktop.h"
 #include "volume.h"
 #include "fork.h"
@@ -53,22 +55,22 @@ char *strchr (), *strrchr ();
 #ifdef DROPKLUDGE
 int matchfile2dirperms(
 /* Since it's kinda' big; I decided against an
-inline function */
-    char       *upath,
+   inline function */
+    char    *upath,
     struct vol  *vol,
-    int                did)
+    int     did)
 /* The below code changes the way file ownership is determined in the name of
-fixing dropboxes.  It has known security problem.  See the netatalk FAQ for
-more information */
+   fixing dropboxes.  It has known security problem.  See the netatalk FAQ for
+   more information */
 {
-    struct stat        st, sb;
-    struct dir *dir;
-    char       *adpath;
+    struct stat st, sb;
+    struct dir  *dir;
+    char    *adpath;
     uid_t       uid;
     int         ret = AFP_OK;
 #ifdef DEBUG
     LOG(log_debug9, logtype_afpd, "begin matchfile2dirperms:");
-#endif 
+#endif
 
     if (stat(upath, &st ) < 0) {
         LOG(log_error, logtype_afpd, "Could not stat %s: %s", upath, strerror(errno));
@@ -120,7 +122,7 @@ more information */
                     adpath, strerror(errno));
                 ret = AFPERR_ACCESS;
             }
-            seteuid(uid); 
+            seteuid(uid);
         }
     } /* end else if stat success */
 
@@ -133,13 +135,13 @@ more information */
 
 int afp_getfildirparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
 {
-    struct stat                *st;
-    struct vol         *vol;
-    struct dir         *dir;
+    struct stat     *st;
+    struct vol      *vol;
+    struct dir      *dir;
     u_int32_t           did;
-    int                        ret;
-    size_t             buflen;
-    u_int16_t          fbitmap, dbitmap, vid;
+    int         ret;
+    size_t      buflen;
+    u_int16_t       fbitmap, dbitmap, vid;
     struct path         *s_path;
 
     *rbuflen = 0;
@@ -150,7 +152,7 @@ int afp_getfildirparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *r
     if (NULL == ( vol = getvolbyvid( vid )) ) {
         /* was AFPERR_PARAM but it helps OS 10.3 when a volume has been removed
          * from the list.
-         */ 
+         */
         return( AFPERR_ACCESS );
     }
 
@@ -169,11 +171,12 @@ int afp_getfildirparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *r
     ibuf += sizeof( dbitmap );
 
     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
-        return get_afp_errno(AFPERR_NOOBJ); 
+        return get_afp_errno(AFPERR_NOOBJ);
     }
 
-    LOG(log_debug, logtype_afpd, "getfildirparams(vid:%u, did:%u, name:'%s', f/d:%04x/%04x) {cwd: %s}",
-        ntohs(vid), ntohl(dir->d_did), s_path->u_name, fbitmap, dbitmap, getcwdpath());
+    LOG(log_debug, logtype_afpd, "getfildirparams(vid:%u, did:%u, f/d:%04x/%04x) {cwdid:%u, cwd: %s, name:'%s'}",
+        ntohs(vid), ntohl(dir->d_did), fbitmap, dbitmap,
+        ntohl(curdir->d_did), cfrombstr(curdir->d_fullpath), s_path->u_name);
 
     st   = &s_path->st;
     if (!s_path->st_valid) {
@@ -185,7 +188,9 @@ int afp_getfildirparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *r
         of_statdir(vol, s_path);
     }
     if ( s_path->st_errno != 0 ) {
-        return( AFPERR_NOOBJ );
+        if (afp_errno != AFPERR_ACCESS) {
+            return( AFPERR_NOOBJ );
+        }
     }
 
 
@@ -193,19 +198,19 @@ int afp_getfildirparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *r
     if (S_ISDIR(st->st_mode)) {
         if (dbitmap) {
             dir = s_path->d_dir;
-            if (!dir) 
+            if (!dir)
                 return AFPERR_NOOBJ;
 
             ret = getdirparams(vol, dbitmap, s_path, dir,
-                                 rbuf + 3 * sizeof( u_int16_t ), &buflen );
+                               rbuf + 3 * sizeof( u_int16_t ), &buflen );
             if (ret != AFP_OK )
                 return( ret );
         }
         /* this is a directory */
         *(rbuf + 2 * sizeof( u_int16_t )) = (char) FILDIRBIT_ISDIR;
     } else {
-        if (fbitmap && AFP_OK != (ret = getfilparams(vol, fbitmap, s_path, curdir, 
-                                            rbuf + 3 * sizeof( u_int16_t ), &buflen )) ) {
+        if (fbitmap && AFP_OK != (ret = getfilparams(vol, fbitmap, s_path, curdir,
+                                                     rbuf + 3 * sizeof( u_int16_t ), &buflen )) ) {
             return( ret );
         }
         /* this is a file */
@@ -225,12 +230,12 @@ int afp_getfildirparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *r
 
 int afp_setfildirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
-    struct stat        *st;
-    struct vol *vol;
-    struct dir *dir;
+    struct stat *st;
+    struct vol  *vol;
+    struct dir  *dir;
     struct path *path;
-    u_int16_t  vid, bitmap;
-    int                did, rc;
+    u_int16_t   vid, bitmap;
+    int     did, rc;
 
     *rbuflen = 0;
     ibuf += 2;
@@ -248,7 +253,7 @@ int afp_setfildirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf
     ibuf += sizeof( did);
 
     if (NULL == ( dir = dirlookup( vol, did )) ) {
-       return afp_errno;    
+        return afp_errno;
     }
 
     memcpy( &bitmap, ibuf, sizeof( bitmap ));
@@ -256,7 +261,7 @@ int afp_setfildirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf
     ibuf += sizeof( bitmap );
 
     if (NULL == ( path = cname( vol, dir, &ibuf ))) {
-        return get_afp_errno(AFPERR_NOOBJ); 
+        return get_afp_errno(AFPERR_NOOBJ);
     }
 
     st   = &path->st;
@@ -268,7 +273,8 @@ int afp_setfildirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf
     }
 
     if ( path->st_errno != 0 ) {
-        return( AFPERR_NOOBJ );
+        if (afp_errno != AFPERR_ACCESS)
+            return( AFPERR_NOOBJ );
     }
     /*
      * If ibuf is odd, make it even.
@@ -289,7 +295,7 @@ int afp_setfildirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf
     return( rc );
 }
 
-/* -------------------------------------------- 
+/* --------------------------------------------
    Factorise some checks on a pathname
 */
 int check_name(const struct vol *vol, char *name)
@@ -323,7 +329,7 @@ static int moveandrename(const struct vol *vol,
                          char *newname,
                          int isdir)
 {
-    char            *p;
+    char            *oldunixname = NULL;
     char            *upath;
     int             rc;
     struct stat     *st, nst;
@@ -333,98 +339,112 @@ static int moveandrename(const struct vol *vol,
     struct ofork       *opened = NULL;
     struct path     path;
     cnid_t          id;
-    int             cwd_fd;
+    int             cwd_fd = -1;
+
+    LOG(log_debug, logtype_afpd,
+        "moveandrename: [\"%s\"/\"%s\"] -> \"%s\"",
+        cfrombstr(sdir->d_u_name), oldname, newname);
 
     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
     adp = &ad;
     adflags = 0;
 
     if (!isdir) {
-        if ((p = mtoupath(vol, oldname, sdir->d_did, utf8_encoding())) == NULL)
+        if ((oldunixname = strdup(mtoupath(vol, oldname, sdir->d_did, utf8_encoding()))) == NULL)
             return AFPERR_PARAM; /* can't convert */
+        id = cnid_get(vol->v_cdb, sdir->d_did, oldunixname, strlen(oldunixname));
 
-#ifndef HAVE_RENAMEAT
+#ifndef HAVE_ATFUNCS
         /* Need full path */
-        id = cnid_get(vol->v_cdb, sdir->d_did, p, strlen(p));
-        p = ctoupath( vol, sdir, oldname );
-        if (!p)
+        free(oldunixname);
+        if ((oldunixname = strdup(ctoupath(vol, sdir, oldname))) == NULL)
             return AFPERR_PARAM; /* pathname too long */
-#endif /* HAVE_RENAMEAT */
+#endif /* HAVE_ATFUNCS */
 
         path.st_valid = 0;
-        path.u_name = p;
-#ifdef HAVE_RENAMEAT
+        path.u_name = oldunixname;
+
+#ifdef HAVE_ATFUNCS
         opened = of_findnameat(sdir_fd, &path);
 #else
         opened = of_findname(&path);
-#endif /* HAVE_RENAMEAT */
+#endif /* HAVE_ATFUNCS */
+
         if (opened) {
             /* reuse struct adouble so it won't break locks */
             adp = opened->of_ad;
         }
     } else {
         id = sdir->d_did; /* we already have the CNID */
-        p = ctoupath( vol, sdir->d_parent, oldname );
-        if (!p) {
+        if ((oldunixname = strdup(ctoupath( vol, dirlookup(vol, sdir->d_pdid), oldname))) == NULL)
             return AFPERR_PARAM;
-        }
         adflags = ADFLAGS_DIR;
     }
 
-
     /*
-     * p now points to either
+     * oldunixname now points to either
      *   a) full pathname of the source fs object (if renameat is not available)
      *   b) the oldname (renameat is available)
      * we are in the dest folder so we need to use 
-     *   a) p for ad_open
+     *   a) oldunixname for ad_open
      *   b) fchdir sdir_fd before eg ad_open or use *at functions where appropiate
      */
 
     if (sdir_fd != -1) {
         if ((cwd_fd = open(".", O_RDONLY)) == -1)
             return AFPERR_MISC;
-        if (fchdir(sdir_fd) != 0)
-            return AFPERR_MISC;
+        if (fchdir(sdir_fd) != 0) {
+            rc = AFPERR_MISC;
+            goto exit;
+        }
     }
-    if (!ad_metadata(p, adflags, adp)) {
+    if (!ad_metadata(oldunixname, adflags, adp)) {
         u_int16_t bshort;
 
         ad_getattr(adp, &bshort);
         ad_close_metadata( adp);
-        if ((bshort & htons(ATTRBIT_NORENAME))) 
-            return(AFPERR_OLOCK);
+        if ((bshort & htons(ATTRBIT_NORENAME))) {
+            rc = AFPERR_OLOCK;
+            goto exit;
+        }
     }
     if (sdir_fd != -1) {
         if (fchdir(cwd_fd) != 0) {
             LOG(log_error, logtype_afpd, "moveandrename: %s", strerror(errno) );
-            return AFPERR_MISC;
+            rc = AFPERR_MISC;
+            goto exit;
         }
     }
 
     if (NULL == (upath = mtoupath(vol, newname, curdir->d_did, utf8_encoding()))){ 
-        return AFPERR_PARAM;
+        rc = AFPERR_PARAM;
+        goto exit;
     }
     path.u_name = upath;
-    st = &path.st;    
+    st = &path.st;
     if (0 != (rc = check_name(vol, upath))) {
-            return  rc;
+        goto exit;
     }
 
     /* source == destination. we just silently accept this. */
-    if ((!isdir && curdir == sdir) || (isdir && curdir == sdir->d_parent)) {
-        if (strcmp(oldname, newname) == 0)
-            return AFP_OK;
+    if ((!isdir && curdir == sdir) || (isdir && curdir->d_did == sdir->d_pdid)) {
+        if (strcmp(oldname, newname) == 0) {
+            rc = AFP_OK;
+            goto exit;
+        }
 
         if (stat(upath, st) == 0 || caseenumerate(vol, &path, curdir) == 0) {
-            if (!stat(p, &nst) && !(nst.st_dev == st->st_dev && nst.st_ino == st->st_ino) ) {
+            if (!stat(oldunixname, &nst) && !(nst.st_dev == st->st_dev && nst.st_ino == st->st_ino) ) {
                 /* not the same file */
-                return AFPERR_EXIST;
+                rc = AFPERR_EXIST;
+                goto exit;
             }
             errno = 0;
         }
-    } else if (stat(upath, st ) == 0 || caseenumerate(vol, &path, curdir) == 0)
-        return AFPERR_EXIST;
+    } else if (stat(upath, st ) == 0 || caseenumerate(vol, &path, curdir) == 0) {
+        rc = AFPERR_EXIST;
+        goto exit;
+    }
 
     if ( !isdir ) {
         path.st_valid = 1;
@@ -432,35 +452,56 @@ static int moveandrename(const struct vol *vol,
         if (of_findname(&path)) {
             rc = AFPERR_EXIST; /* was AFPERR_BUSY; */
         } else {
-            rc = renamefile(vol, sdir_fd, p, upath, newname, adp );
+            rc = renamefile(vol, sdir_fd, oldunixname, upath, newname, adp );
             if (rc == AFP_OK)
                 of_rename(vol, opened, sdir, oldname, curdir, newname);
         }
     } else {
-        rc = renamedir(vol, sdir_fd, p, upath, sdir, curdir, newname);
+        rc = renamedir(vol, sdir_fd, oldunixname, upath, sdir, curdir, newname);
     }
     if ( rc == AFP_OK && id ) {
         /* renaming may have moved the file/dir across a filesystem */
-        if (stat(upath, st) < 0)
-            return AFPERR_MISC;
+        if (stat(upath, st) < 0) {
+            rc = AFPERR_MISC;
+            goto exit;
+        }
+
+        /* Remove it from the cache */
+        struct dir *cacheddir = dircache_search_by_did(vol, id);
+        if (cacheddir) {
+            LOG(log_warning, logtype_afpd,"Still cached: \"%s/%s\"", getcwdpath(), upath);
+            (void)dir_remove(vol, cacheddir);
+        }
+
+        /* Fixup adouble info */
+        if (!ad_metadata(upath, adflags, adp)) {
+            ad_setid(adp, st->st_dev, st->st_ino, id, curdir->d_did, vol->v_stamp);
+            ad_flush(adp);
+            ad_close_metadata(adp);
+        }
 
         /* fix up the catalog entry */
         cnid_update(vol->v_cdb, id, st, curdir->d_did, upath, strlen(upath));
     }
 
+exit:
+    if (cwd_fd != -1)
+        close(cwd_fd);
+    if (oldunixname)
+        free(oldunixname);
     return rc;
 }
 
 /* -------------------------------------------- */
 int afp_rename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
-    struct vol *vol;
-    struct dir *sdir;
+    struct vol  *vol;
+    struct dir  *sdir;
     char        *oldname, *newname;
     struct path *path;
-    u_int32_t  did;
+    u_int32_t   did;
     int         plen;
-    u_int16_t  vid;
+    u_int16_t   vid;
     int         isdir = 0;
     int         rc;
 
@@ -479,12 +520,12 @@ int afp_rename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
     memcpy( &did, ibuf, sizeof( did ));
     ibuf += sizeof( did );
     if (NULL == ( sdir = dirlookup( vol, did )) ) {
-       return afp_errno;    
+        return afp_errno;
     }
 
     /* source pathname */
     if (NULL == ( path = cname( vol, sdir, &ibuf )) ) {
-        return get_afp_errno(AFPERR_NOOBJ); 
+        return get_afp_errno(AFPERR_NOOBJ);
     }
 
     sdir = curdir;
@@ -499,14 +540,14 @@ int afp_rename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
         }
     }
     else {
-        if ( sdir->d_parent == NULL ) { /* root directory */
+        if ( sdir->d_did == DIRDID_ROOT ) { /* root directory */
             return( AFPERR_NORENAME );
         }
         /* move to destination dir */
-        if ( movecwd( vol, sdir->d_parent ) < 0 ) {
+        if ( movecwd( vol, dirlookup(vol, sdir->d_pdid) ) < 0 ) {
             return afp_errno;
         }
-        strcpy(oldname, sdir->d_m_name);
+        memcpy(oldname, cfrombstr(sdir->d_m_name), blength(sdir->d_m_name) +1);
     }
 
     /* another place where we know about the path type */
@@ -529,12 +570,12 @@ int afp_rename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
 /* ------------------------------- */
 int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
-    struct vol         *vol;
-    struct dir         *dir;
+    struct vol      *vol;
+    struct dir      *dir;
     struct path         *s_path;
-    char               *upath;
-    int                        did, rc;
-    u_int16_t          vid;
+    char        *upath;
+    int         did, rc;
+    u_int16_t       vid;
 
     *rbuflen = 0;
     ibuf += 2;
@@ -550,29 +591,27 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
 
     memcpy( &did, ibuf, sizeof( did ));
     ibuf += sizeof( int );
+
     if (NULL == ( dir = dirlookup( vol, did )) ) {
-       return afp_errno;    
+        return afp_errno;
     }
 
     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
-        return get_afp_errno(AFPERR_NOOBJ); 
+        return get_afp_errno(AFPERR_NOOBJ);
     }
 
     upath = s_path->u_name;
     if ( path_isadir( s_path) ) {
-       if (*s_path->m_name != '\0') {
-           rc = AFPERR_ACCESS;
-       }
-       else 
-               {
-
-                       /* we have to cache this, the structs are lost in deletcurdir*/
-                       /* but we need the positive returncode to send our event */
-                       char dname[256];
-                       strncpy(dname,  curdir->d_u_name, 255 );
-            if ((rc = deletecurdir(vol)) == AFP_OK)
-                               fce_register_delete_dir(dname);
-        }
+        if (*s_path->m_name != '\0' || curdir->d_did == DIRDID_ROOT)
+            rc = AFPERR_ACCESS;
+    } else  {
+        /* we have to cache this, the structs are lost in deletcurdir*/
+        /* but we need the positive returncode to send our event */
+        char dname[256];
+        strncpy(dname,  curdir->d_u_name, 255 );
+        if ((rc = deletecurdir(vol)) == AFP_OK)
+            fce_register_delete_dir(dname);
+    }
     } else if (of_findname(s_path)) {
         rc = AFPERR_BUSY;
     } else {
@@ -585,10 +624,16 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
         } else {
             if ((rc = deletefile(vol, -1, upath, 1)) === AFP_OK)
                                fce_register_delete_file( s_path );
+
+            struct dir *cachedfile;
+            if ((cachedfile = dircache_search_by_name(vol, dir, upath, strlen(upath), s_path->st.st_ctime))) {
+                dircache_remove(vol, cachedfile, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX);
+                dir_free(cachedfile);
+            }
         }
     }
     if ( rc == AFP_OK ) {
-       curdir->offcnt--;
+        curdir->offcnt--;
         setvoltime(obj, vol );
     }
 
@@ -597,66 +642,51 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
 /* ------------------------ */
 char *absupath(const struct vol *vol, struct dir *dir, char *u)
 {
-    struct dir *d;
-    static char        path[ MAXPATHLEN + 1];
-    char       *p;
-    int                len;
+    static char pathbuf[MAXPATHLEN + 1];
+    bstring path;
 
-    if (u == NULL)
+    if (u == NULL || dir == NULL || vol == NULL)
         return NULL;
-        
-    p = path + sizeof( path ) - 1;
-    *p = '\0';
-    len = strlen( u );
-    p -= len;
-    memcpy( p, u, len );
-    if (dir) for ( d = dir; d->d_parent; d = d->d_parent ) {
-        u = d->d_u_name;
-        len = strlen( u );
-        if (p -len -1 < path) {
-            /* FIXME 
-               rather rare so LOG error and/or client message ?
-            */
-            return NULL;
-        }
-        *--p = '/';
-        p -= len;
-        memcpy( p, u, len );
-    }
-    len = strlen( vol->v_path );
-    if (p -len -1 < path) {
+
+    if ((path = bstrcpy(dir->d_fullpath)) == NULL)
         return NULL;
-    }
-    *--p = '/';
-    p -= len;
-    memcpy( p, vol->v_path, len );
+    if (bcatcstr(path, "/") != BSTR_OK)
+        return NULL;
+    if (bcatcstr(path, u) != BSTR_OK)
+        return NULL;
+    if (path->slen > MAXPATHLEN)
+        return NULL;
+
+    LOG(log_debug, logtype_afpd, "absupath: %s", cfrombstr(path));
 
-    return( p );
+    strncpy(pathbuf, cfrombstr(path), blength(path) + 1);
+    bdestroy(path);
+
+    return(pathbuf);
 }
 
-/* ------------------------
- * FIXME dir could be NULL
-*/
 char *ctoupath(const struct vol *vol, struct dir *dir, char *name)
 {
+    if (vol == NULL || dir == NULL || name == NULL)
+        return NULL;
     return absupath(vol, dir, mtoupath(vol, name, dir->d_did, utf8_encoding()));
 }
 
 /* ------------------------- */
 int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
-    struct vol *vol;
-    struct dir *sdir, *ddir;
+    struct vol  *vol;
+    struct dir  *sdir, *ddir;
     int         isdir;
-    char       *oldname, *newname;
+    char    *oldname, *newname;
     struct path *path;
-    int                did;
-    int                pdid;
+    int     did;
+    int     pdid;
     int         plen;
-    u_int16_t  vid;
+    u_int16_t   vid;
     int         rc;
 #ifdef DROPKLUDGE
-    int                retvalue;
+    int     retvalue;
 #endif /* DROPKLUDGE */
     int     sdir_fd = -1;
 
@@ -685,13 +715,13 @@ int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
 
     /* source pathname */
     if (NULL == ( path = cname( vol, sdir, &ibuf )) ) {
-        return get_afp_errno(AFPERR_NOOBJ); 
+        return get_afp_errno(AFPERR_NOOBJ);
     }
 
     sdir = curdir;
     newname = obj->newtmp;
     oldname = obj->oldtmp;
-    
+
     isdir = path_isadir(path);
     if ( *path->m_name != '\0' ) {
         if (isdir) {
@@ -699,10 +729,10 @@ int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
         }
         strcpy(oldname, path->m_name); /* an extra copy for of_rename */
     } else {
-        strcpy(oldname, sdir->d_m_name);
+        memcpy(oldname, cfrombstr(sdir->d_m_name), blength(sdir->d_m_name) + 1);
     }
 
-#ifdef HAVE_RENAMEAT
+#ifdef HAVE_ATFUNCS
     if ((sdir_fd = open(".", O_RDONLY)) == -1)
         return AFPERR_MISC;
 #endif
@@ -733,11 +763,13 @@ int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
     }
 
     /* This does the work */
+    LOG(log_debug, logtype_afpd, "afp_move(oldname:'%s', newname:'%s', isdir:%u)",
+        oldname, newname, isdir);
     rc = moveandrename(vol, sdir, sdir_fd, oldname, newname, isdir);
 
     if ( rc == AFP_OK ) {
         char *upath = mtoupath(vol, newname, pdid, utf8_encoding());
-        
+
         if (NULL == upath) {
             rc = AFPERR_PARAM;
             goto exit;
@@ -765,7 +797,7 @@ int afp_moveandrename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
     }
 
 exit:
-#ifdef HAVE_RENAMEAT
+#ifdef HAVE_ATFUNCS
     if (sdir_fd != -1)
         close(sdir_fd);
 #endif
@@ -780,8 +812,8 @@ int veto_file(const char*veto_str, const char*path)
  * otherwise, 0 is returned.
  */
 {
-    int i;     /* index to veto_str */
-    int j;     /* index to path */
+    int i;  /* index to veto_str */
+    int j;  /* index to path */
 
     if ((veto_str == NULL) || (path == NULL))
         return 0;
@@ -796,7 +828,7 @@ int veto_file(const char*veto_str, const char*path)
         } else {
             if (veto_str[i] != path[j]) {
                 while ((veto_str[i] != '/')
-                        && (veto_str[i] != '\0'))
+                       && (veto_str[i] != '\0'))
                     i++;
                 j = 0;
                 continue;
index 78d965ee50fc98c18c9c2c0a7328998f28fc13a2..f7d9f82a839fb2412c3b514bf3df3e5fa06e0826 100644 (file)
@@ -73,7 +73,7 @@ static int getforkparams(struct ofork *ofork, u_int16_t bitmap, char *buf, size_
     }
 
     vol = ofork->of_vol;
-    dir = ofork->of_dir;
+    dir = dirlookup(vol, ofork->of_did);
 
     if (NULL == (path.u_name = mtoupath(vol, of_name(ofork), dir->d_did, utf8_encoding()))) {
         return( AFPERR_MISC );
@@ -433,7 +433,8 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si
                 goto openfork_err;
                 break;
             default:
-                LOG(log_error, logtype_afpd, "afp_openfork(%s): ad_open: %s", s_path->m_name, strerror(errno) );
+                LOG(log_error, logtype_afpd, "afp_openfork('%s/%s'): ad_open: errno: %i (%s)",
+                    getcwdpath, s_path->m_name, errno, strerror(errno) );
                 goto openfork_err;
                 break;
             }
index c4e25de1696faee8f57f90d93590ce7f38515906..ae22b8f7f6b52c1b161808ac027b07948680af97 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: fork.h,v 1.18 2010-03-12 15:16:49 franklahm Exp $
- *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
  */
 #include "directory.h"
 
 struct file_key {
-    dev_t              dev;
-    ino_t              inode;
+    dev_t       dev;
+    ino_t       inode;
 };
 
 struct ofork {
     struct file_key     key;
-    struct adouble     *of_ad;
+    struct adouble      *of_ad;
     struct vol          *of_vol;
-    struct dir         *of_dir;
-
-    u_int16_t           of_refnum;
+    cnid_t              of_did;
+    uint16_t            of_refnum;
     int                 of_flags;
-
     struct ofork        **prevp, *next;
-    struct ofork        *of_d_prev, *of_d_next;
+//    struct ofork        *of_d_prev, *of_d_next;
 };
 
-#define OPENFORK_DATA  (0)
-#define OPENFORK_RSCS  (1<<7)
+#define OPENFORK_DATA   (0)
+#define OPENFORK_RSCS   (1<<7)
 
-#define OPENACC_RD     (1<<0)
-#define OPENACC_WR     (1<<1)
-#define OPENACC_DRD    (1<<4)
-#define OPENACC_DWR    (1<<5)
+#define OPENACC_RD  (1<<0)
+#define OPENACC_WR  (1<<1)
+#define OPENACC_DRD (1<<4)
+#define OPENACC_DWR (1<<5)
 
 /* ofork.of_flags bits */
-#define AFPFORK_OPEN   (1<<0)
-#define AFPFORK_RSRC   (1<<1)
-#define AFPFORK_DATA   (1<<2)
+#define AFPFORK_OPEN    (1<<0)
+#define AFPFORK_RSRC    (1<<1)
+#define AFPFORK_DATA    (1<<2)
 #define AFPFORK_DIRTY   (1<<3)
 #define AFPFORK_ACCRD   (1<<4)
 #define AFPFORK_ACCWR   (1<<5)
@@ -75,12 +71,13 @@ extern int          of_stat      (struct path *);
 extern int          of_statdir   (struct vol *vol, struct path *);
 extern int          of_closefork (struct ofork *ofork);
 extern void         of_closevol  (const struct vol *vol);
+extern void         of_close_all_forks(void);
 extern struct adouble *of_ad     (const struct vol *, struct path *, struct adouble *);
 
-#ifdef HAVE_RENAMEAT
+#ifdef HAVE_ATFUNCS
 extern struct ofork *of_findnameat(int dirfd, struct path *path);
 extern int of_fstatat(int dirfd, struct path *path);
-#endif  /* HAVE_RENAMEAT */
+#endif  /* HAVE_ATFUNCS */
 
 
 /* in fork.c */
index 40af2fe1d756b9ea92bb620462ef8d80b4f679fb..02dcf8b56b58abd1dd00d02dbe44452a2c19a3e4 100644 (file)
 #include <atalk/unicode.h>
 #include <atalk/uam.h>
 
-#define MACFILELEN 31
+/* #define DOSFILELEN 12 */             /* Type1, DOS-compat*/
+#define MACFILELEN 31                   /* Type2, HFS-compat */
+#define UTF8FILELEN_EARLY 255           /* Type3, early Mac OS X 10.0-10.4.? */
+/* #define UTF8FILELEN_NAME_MAX 765 */  /* Type3, 10.4.?- , getconf NAME_MAX */
+/* #define UTF8FILELEN_SPEC 0xFFFF */   /* Type3, spec on document */
+/* #define HFSPLUSFILELEN 510 */        /* HFS+ spec, 510byte = 255codepoint */
+
 #define MAXUSERLEN 256
 
 #define OPTION_DEBUG         (1 << 0)
@@ -35,6 +41,8 @@
 #define OPTION_NOSLP         (1 << 5)
 #define OPTION_ANNOUNCESSH   (1 << 6)
 #define OPTION_UUID          (1 << 7)
+#define OPTION_ACL2MACCESS   (1 << 8)
+#define OPTION_NOZEROCONF    (1 << 9)
 
 #ifdef FORCE_UIDGID
 /* set up a structure for this */
@@ -54,14 +62,19 @@ struct afp_volume_name {
 };
 
 struct afp_options {
-    int connections, transports, tickleval, timeout, server_notif, flags;
+    int connections, transports, tickleval, timeout, server_notif, flags, dircachesize;
+    int sleep;                  /* Maximum time allowed to sleep (in tickles) */
+    int disconnected;           /* Maximum time in disconnected state (in tickles) */
+    unsigned int tcp_sndbuf, tcp_rcvbuf;
     unsigned char passwdbits, passwdminlen, loginmaxfail;
     u_int32_t server_quantum;
+    int dsireadbuf; /* scale factor for sizefof(dsi->buffer) = server_quantum * dsireadbuf */
     char hostname[MAXHOSTNAMELEN + 1], *server, *ipaddr, *port, *configfile;
     struct at_addr ddpaddr;
     char *uampath, *fqdn;
     char *pidfile;
     char *sigconffile;
+    char *uuidconf;
     struct afp_volume_name defaultvol, systemvol, uservol;
     int  closevol;
 
@@ -75,7 +88,6 @@ struct afp_options {
     charset_t maccharset, unixcharset; 
     mode_t umask;
     mode_t save_mask;
-    int    sleep;
 #ifdef ADMIN_GRP
     gid_t admingid;
 #endif /* ADMIN_GRP */
@@ -83,26 +95,27 @@ struct afp_options {
 
     /* default value for winbind authentication */
     char *ntdomain, *ntseparator;
+    char *logconfig;
 };
 
 #define AFPOBJ_TMPSIZ (MAXPATHLEN)
 typedef struct _AFPObj {
     int proto;
     unsigned long servernum;
-    void *handle, *config;
+    void *handle;               /* either (DSI *) or (ASP *) */
+    void *config; 
     struct afp_options options;
     char *Obj, *Type, *Zone;
     char username[MAXUSERLEN];
     void (*logout)(void), (*exit)(int);
     int (*reply)(void *, int);
     int (*attention)(void *, AFPUserBytes);
-    void (*sleep)(void);
     /* to prevent confusion, only use these in afp_* calls */
     char oldtmp[AFPOBJ_TMPSIZ + 1], newtmp[AFPOBJ_TMPSIZ + 1];
     void *uam_cookie; /* cookie for uams */
     struct session_info  sinfo;
     uid_t uid;         /* client running user id */
-
+    int ipc_fd; /* anonymous PF_UNIX socket for IPC with afpd parent */
 #ifdef FORCE_UIDGID
     int                 force_uid;
     uidgidset          uidgid;
@@ -142,6 +155,9 @@ extern int  parseline  (int, char *);
 extern const char *AfpNum2name (int );
 extern const char *AfpErr2name(int err);
 
+/* directory.c */
+extern struct dir rootParent;
+
 #ifndef NO_DDP
 extern void afp_over_asp (AFPObj *);
 #endif /* NO_DDP */
index fd69d284ea61b3f82f8a43900a354139cf3e6f07..b7471ccb19ec821b7b7a6559272b02ec6736c985 100644 (file)
@@ -854,7 +854,7 @@ static hash_val_t hash_fun2(const void *key)
 {
     int len, rem;
     const unsigned char *data = key;
-    hash_val_t hash, tmp;
+    hash_val_t hash = 0, tmp = 0;
 
     len = strlen((char *)data);
 
index ffe409e08e05b608fb55b23e5328e7cb47496161..0d552d6f73c77faa3478f4a37702fb630aa6e7a3 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: main.c,v 1.26 2009-10-14 02:24:05 didg Exp $
- *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
  */
@@ -19,8 +17,9 @@
 #include <atalk/logger.h>
 #include <sys/time.h>
 #include <sys/socket.h>
-
+#include <sys/poll.h>
 #include <errno.h>
+#include <sys/wait.h>
 
 #include <atalk/adouble.h>
 
@@ -40,6 +39,7 @@
 #include "status.h"
 #include "fork.h"
 #include "uam_auth.h"
+#include "afp_zeroconf.h"
 
 #ifdef TRU64
 #include <sys/security.h>
@@ -55,8 +55,14 @@ unsigned char        nologin = 0;
 struct afp_options default_options;
 static AFPConfig *configs;
 static server_child *server_children;
-static fd_set save_rfds;
-static int    Ipc_fd = -1;
+static sig_atomic_t reloadconfig = 0;
+
+/* Two pointers to dynamic allocated arrays which store pollfds and associated data */
+static struct pollfd *fdset;
+static struct polldata *polldata;
+static int fdset_size;          /* current allocated size */
+static int fdset_used;          /* number of used elements */
+
 
 #ifdef TRU64
 void afp_get_cmdline( int *ac, char ***av)
@@ -66,30 +72,41 @@ void afp_get_cmdline( int *ac, char ***av)
 }
 #endif /* TRU64 */
 
-static void afp_exit(const int i)
+/* This is registered with atexit() */
+static void afp_exit(void)
 {
-    server_unlock(default_options.pidfile);
-    exit(i);
+    if (parent_or_child == 0)
+        /* Only do this in the parent */
+        server_unlock(default_options.pidfile);
 }
 
+
 /* ------------------
    initialize fd set we are waiting for.
 */
-static void set_fd(int ipc_fd)
+static void fd_set_listening_sockets(void)
 {
     AFPConfig   *config;
 
-    FD_ZERO(&save_rfds);
     for (config = configs; config; config = config->next) {
         if (config->fd < 0) /* for proxies */
             continue;
-        FD_SET(config->fd, &save_rfds);
-    }
-    if (ipc_fd >= 0) {
-        FD_SET(ipc_fd, &save_rfds);
+        fdset_add_fd(&fdset, &polldata, &fdset_used, &fdset_size, config->fd, LISTEN_FD, config);
     }
 }
  
+static void fd_reset_listening_sockets(void)
+{
+    AFPConfig   *config;
+
+    for (config = configs; config; config = config->next) {
+        if (config->fd < 0) /* for proxies */
+            continue;
+        fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, config->fd);
+    }
+    fd_set_listening_sockets();
+}
+
 /* ------------------ */
 static void afp_goaway(int sig)
 {
@@ -98,58 +115,68 @@ static void afp_goaway(int sig)
     asp_kill(sig);
 #endif /* ! NO_DDP */
 
-    dsi_kill(sig);
+    if (server_children)
+        server_child_kill(server_children, CHILD_DSIFORK, sig);
+
     switch( sig ) {
+
     case SIGTERM :
-        LOG(log_info, logtype_afpd, "shutting down on signal %d", sig );
+        LOG(log_note, logtype_afpd, "AFP Server shutting down on SIGTERM");
+        AFPConfig *config;
+        for (config = configs; config; config = config->next)
+            if (config->server_cleanup)
+                config->server_cleanup(config);
+        server_unlock(default_options.pidfile);
+        exit(0);
         break;
+
     case SIGUSR1 :
-    case SIGHUP :
-        /* w/ a configuration file, we can force a re-read if we want */
         nologin++;
         auth_unload();
-        if (sig == SIGHUP || ((nologin + 1) & 1)) {
-            AFPConfig *config;
-
-            LOG(log_info, logtype_afpd, "re-reading configuration file");
-            for (config = configs; config; config = config->next)
-                if (config->server_cleanup)
-                    config->server_cleanup(config);
+        LOG(log_info, logtype_afpd, "disallowing logins");        
+        break;
 
-            /* configfree close atp socket used for DDP tickle, there's an issue
-             * with atp tid.
-            */
-            configfree(configs, NULL);
-            if (!(configs = configinit(&default_options))) {
-                LOG(log_error, logtype_afpd, "config re-read: no servers configured");
-                afp_exit(EXITERR_CONF);
-            }
-            set_fd(Ipc_fd);
-        } else {
-            LOG(log_info, logtype_afpd, "disallowing logins");
-        }
-        if (sig == SIGHUP) {
-            nologin = 0;
-        }
+    case SIGHUP :
+        /* w/ a configuration file, we can force a re-read if we want */
+        reloadconfig = 1;
         break;
+
     default :
         LOG(log_error, logtype_afpd, "afp_goaway: bad signal" );
     }
-    if ( sig == SIGTERM ) {
-        AFPConfig *config;
-
-        for (config = configs; config; config = config->next)
-            if (config->server_cleanup)
-                config->server_cleanup(config);
-
-        afp_exit(0);
-    }
     return;
 }
 
 static void child_handler(int sig _U_)
 {
-    server_child_handler(server_children);
+    int fd;
+    int status, i;
+    pid_t pid;
+  
+#ifndef WAIT_ANY
+#define WAIT_ANY (-1)
+#endif /* ! WAIT_ANY */
+
+    while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
+        for (i = 0; i < server_children->nforks; i++) {
+            if ((fd = server_child_remove(server_children, i, pid)) != -1) {
+                fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, fd);        
+                break;
+            }
+        }
+
+        if (WIFEXITED(status)) {
+            if (WEXITSTATUS(status))
+                LOG(log_info, logtype_afpd, "child[%d]: exited %d", pid, WEXITSTATUS(status));
+            else
+                LOG(log_info, logtype_afpd, "child[%d]: done", pid);
+        } else {
+            if (WIFSIGNALED(status))
+                LOG(log_info, logtype_afpd, "child[%d]: killed by signal %d", pid, WTERMSIG(status));
+            else
+                LOG(log_info, logtype_afpd, "child[%d]: died", pid);
+        }
+    }
 }
 
 int main(int ac, char **av)
@@ -167,9 +194,12 @@ int main(int ac, char **av)
     set_auth_parameters( ac, av );
 #endif /* TRU64 */
 
-#ifdef DEBUG1
+    /* Log SIGBUS/SIGSEGV SBT */
     fault_setup(NULL);
-#endif
+
+    /* Default log setup: log to syslog */
+    setuplog("default log_note");
+
     afp_options_init(&default_options);
     if (!afp_options_parse(ac, av, &default_options))
         exit(EXITERR_CONF);
@@ -187,11 +217,7 @@ int main(int ac, char **av)
     default: /* server */
         exit(0);
     }
-
-#if 0
-    /* Register CNID  */
-    cnid_init();
-#endif
+    atexit(afp_exit);
 
     /* install child handler for asp and dsi. we do this before afp_goaway
      * as afp_goaway references stuff from here. 
@@ -199,18 +225,21 @@ int main(int ac, char **av)
     if (!(server_children = server_child_alloc(default_options.connections,
                             CHILD_NFORKS))) {
         LOG(log_error, logtype_afpd, "main: server_child alloc: %s", strerror(errno) );
-        afp_exit(EXITERR_SYS);
+        exit(EXITERR_SYS);
     }
-    
-#ifdef AFP3x
+
+    memset(&sv, 0, sizeof(sv));    
     /* linux at least up to 2.4.22 send a SIGXFZ for vfat fs,
        even if the file is open with O_LARGEFILE ! */
 #ifdef SIGXFSZ
-    signal(SIGXFSZ , SIG_IGN); 
+    sv.sa_handler = SIG_IGN;
+    sigemptyset( &sv.sa_mask );
+    if (sigaction(SIGXFSZ, &sv, NULL ) < 0 ) {
+        LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
+        exit(EXITERR_SYS);
+    }
 #endif
-#endif    
     
-    memset(&sv, 0, sizeof(sv));
     sv.sa_handler = child_handler;
     sigemptyset( &sv.sa_mask );
     sigaddset(&sv.sa_mask, SIGALRM);
@@ -221,7 +250,7 @@ int main(int ac, char **av)
     sv.sa_flags = SA_RESTART;
     if ( sigaction( SIGCHLD, &sv, NULL ) < 0 ) {
         LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
-        afp_exit(EXITERR_SYS);
+        exit(EXITERR_SYS);
     }
 
     sv.sa_handler = afp_goaway;
@@ -233,7 +262,7 @@ int main(int ac, char **av)
     sv.sa_flags = SA_RESTART;
     if ( sigaction( SIGUSR1, &sv, NULL ) < 0 ) {
         LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
-        afp_exit(EXITERR_SYS);
+        exit(EXITERR_SYS);
     }
 
     sigemptyset( &sv.sa_mask );
@@ -244,7 +273,7 @@ int main(int ac, char **av)
     sv.sa_flags = SA_RESTART;
     if ( sigaction( SIGHUP, &sv, NULL ) < 0 ) {
         LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
-        afp_exit(EXITERR_SYS);
+        exit(EXITERR_SYS);
     }
 
 
@@ -256,7 +285,7 @@ int main(int ac, char **av)
     sv.sa_flags = SA_RESTART;
     if ( sigaction( SIGTERM, &sv, NULL ) < 0 ) {
         LOG(log_error, logtype_afpd, "main: sigaction: %s", strerror(errno) );
-        afp_exit(EXITERR_SYS);
+        exit(EXITERR_SYS);
     }
 
     /* afpd.conf: not in config file: lockfile, connections, configfile
@@ -277,21 +306,20 @@ int main(int ac, char **av)
 #endif
     sigaddset(&sigs, SIGCHLD);
 
-    sigprocmask(SIG_BLOCK, &sigs, NULL);
+    pthread_sigmask(SIG_BLOCK, &sigs, NULL);
     if (!(configs = configinit(&default_options))) {
         LOG(log_error, logtype_afpd, "main: no servers configured");
-        afp_exit(EXITERR_CONF);
+        exit(EXITERR_CONF);
     }
-    sigprocmask(SIG_UNBLOCK, &sigs, NULL);
+    pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
 
     /* Register CNID  */
     cnid_init();
 
     /* watch atp, dsi sockets and ipc parent/child file descriptor. */
-    if ((ipc = server_ipc_create())) {
-        Ipc_fd = server_ipc_parent(ipc);
-    }
-    set_fd(Ipc_fd);
+    fd_set_listening_sockets();
+
+    afp_child_t *child;
 
     /* wait for an appleshare connection. parent remains in the loop
      * while the children get handled by afp_over_{asp,dsi}.  this is
@@ -300,27 +328,71 @@ int main(int ac, char **av)
      * afterwards. establishing timeouts for logins is a possible 
      * solution. */
     while (1) {
-        rfds = save_rfds;
-        sigprocmask(SIG_UNBLOCK, &sigs, NULL);
-        ret = select(FD_SETSIZE, &rfds, NULL, NULL, NULL);
-        sigprocmask(SIG_BLOCK, &sigs, NULL);
+        LOG(log_maxdebug, logtype_afpd, "main: polling %i fds", fdset_used);
+        pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
+        ret = poll(fdset, fdset_used, -1);
+        pthread_sigmask(SIG_BLOCK, &sigs, NULL);
+        int saveerrno = errno;
+
+        if (reloadconfig) {
+            nologin++;
+            auth_unload();
+
+            LOG(log_info, logtype_afpd, "re-reading configuration file");
+            for (config = configs; config; config = config->next)
+                if (config->server_cleanup)
+                    config->server_cleanup(config);
+
+            /* configfree close atp socket used for DDP tickle, there's an issue
+             * with atp tid. */
+            configfree(configs, NULL);
+            if (!(configs = configinit(&default_options))) {
+                LOG(log_error, logtype_afpd, "config re-read: no servers configured");
+                exit(EXITERR_CONF);
+            }
+            fd_reset_listening_sockets();
+            nologin = 0;
+            reloadconfig = 0;
+            errno = saveerrno;
+        }
+
+        if (ret == 0)
+            continue;
+        
         if (ret < 0) {
             if (errno == EINTR)
                 continue;
             LOG(log_error, logtype_afpd, "main: can't wait for input: %s", strerror(errno));
             break;
         }
-        if (Ipc_fd >=0 && FD_ISSET(Ipc_fd, &rfds)) {
-            server_ipc_read(server_children);
-        }
-        for (config = configs; config; config = config->next) {
-            if (config->fd < 0)
-                continue;
-            if (FD_ISSET(config->fd, &rfds)) {
-                config->server_start(config, configs, server_children);
-            }
-        }
-    }
+
+        for (int i = 0; i < fdset_used; i++) {
+            if (fdset[i].revents & POLLIN) {
+                switch (polldata[i].fdtype) {
+                case LISTEN_FD:
+                    config = (AFPConfig *)polldata[i].data;
+                    /* config->server_start is afp_config.c:dsi_start() for DSI */
+                    if (child = config->server_start(config, configs, server_children)) {
+                        /* Add IPC fd to select fd set */
+                        fdset_add_fd(&fdset, &polldata, &fdset_used, &fdset_size, child->ipc_fds[0], IPC_FD, child);
+                    }
+                    break;
+                case IPC_FD:
+                    child = (afp_child_t *)polldata[i].data;
+                    LOG(log_debug, logtype_afpd, "main: IPC request from child[%u]", child->pid);
+                    if ((ret = ipc_server_read(server_children, child->ipc_fds[0])) == 0) {
+                        fdset_del_fd(&fdset, &polldata, &fdset_used, &fdset_size, child->ipc_fds[0]);
+                        close(child->ipc_fds[0]);
+                        child->ipc_fds[0] = -1;
+                    }
+                    break;
+                default:
+                    LOG(log_debug, logtype_afpd, "main: IPC request for unknown type");
+                    break;
+                } /* switch */
+            }  /* if */
+        } /* for (i)*/
+    } /* while (1) */
 
     return 0;
 }
index c00b76ba4c874c51227ef2100e64ce9d8a660bcf..cb2b60ed0c15e5bd63bb0cb1ca1c66e7a02089ba 100644 (file)
@@ -1,6 +1,4 @@
 /* 
- * $Id: mangle.c,v 1.19 2006-09-19 01:35:45 didg Exp $ 
- *
  * Copyright (c) 2002. Joe Marcus Clarke (marcus@marcuscom.com)
  * All Rights Reserved.  See COPYRIGHT.
  *
 
 #include <stdio.h>
 #include <ctype.h>
+
+#include <atalk/util.h>
+#include <atalk/bstradd.h>
+
 #include "mangle.h"
 #include "desktop.h"
-#include <atalk/util.h>  
+
 
 #define hextoint( c )   ( isdigit( c ) ? c - '0' : c + 10 - 'A' )
 #define isuxdigit(x)    (isdigit(x) || (isupper(x) && isxdigit(x)))
@@ -38,7 +40,7 @@ static size_t mangle_extension(const struct vol *vol, const char* uname,
   return 0;
 }
 
-static char *demangle_checks ( const struct vol *vol, char* uname, char * mfilename, size_t prefix, char * ext)
+static char *demangle_checks(const struct vol *vol, char* uname, char * mfilename, size_t prefix, char * ext)
 {
     u_int16_t flags;
     static char buffer[MAXPATHLEN +2];  /* for convert_charset dest_len parameter +2 */
@@ -48,18 +50,18 @@ static char *demangle_checks ( const struct vol *vol, char* uname, char * mfilen
     /* We need to check, whether we really need to demangle the filename       */
     /* i.e. it's not just a file with a valid #HEX in the name ...             */
     /* but we don't want to miss valid demangle as well.                       */
-
     /* check whether file extensions match */
-    {
-      char buf[MAX_EXT_LENGTH + 2];  /* for convert_charset dest_len parameter +2 */
-      size_t ext_len = mangle_extension(vol, uname, buf, CH_UTF8_MAC);
-
-      if (ext_len) {
-       buf[ext_len] = '\0';
-       if (strcmp(ext, buf)) return mfilename;
-      } else {
-       if (*ext) return mfilename;
-      }
+
+    char buf[MAX_EXT_LENGTH + 2];  /* for convert_charset dest_len parameter +2 */
+    size_t ext_len = mangle_extension(vol, uname, buf, CH_UTF8_MAC);
+
+    if (ext_len) {
+        buf[ext_len] = '\0';
+        if (strcmp(ext, buf))
+            return mfilename;
+    } else {
+        if (*ext)
+            return mfilename;
     }
 
     /* First we convert the unix name to our volume maccharset         */
@@ -169,8 +171,8 @@ private_demangle(const struct vol *vol, char *mfilename, cnid_t did, cnid_t *osx
     }
 
     /* is it a dir?, there's a conflict with pre OSX 'trash #2'  */
-    if ((dir = dirsearch(vol, id))) {
-        if (dir->d_parent && dir->d_parent->d_did != did) {
+    if ((dir = dirlookup(vol, id))) {
+        if (dir->d_pdid != did) {
             /* not in the same folder, there's a race with outdate cache
              * but we have to live with it, hopefully client will recover
             */
@@ -178,13 +180,12 @@ private_demangle(const struct vol *vol, char *mfilename, cnid_t did, cnid_t *osx
         }
         if (!osx) {
             /* it's not from cname so mfilename and dir must be the same */
-            if (!strcmp(dir->d_m_name, mfilename)) {
-                return dir->d_u_name;
+            if (strcmp(cfrombstr(dir->d_m_name), mfilename) == 0) {
+                return cfrombstr(dir->d_u_name);
             }
-        } 
-        else {
-           return demangle_checks (vol, dir->d_u_name, mfilename, prefix, t);
-       }
+        } else {
+            return demangle_checks(vol, cfrombstr(dir->d_u_name), mfilename, prefix, t);
+        }
     }
     else if (NULL != (u_name = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
         if (id != did) {
@@ -222,13 +223,22 @@ demangle_osx(const struct vol *vol, char *mfilename, cnid_t did, cnid_t *fileid)
     return private_demangle(vol, mfilename, did, fileid);
 }
 
+/* -------------------------------------------------------
+   FIXME !!!
+
+   Early Mac OS X (10.0-10.4.?) had the limitation up to 255 Byte.
+   Current implementation is:
+      volcharset -> UTF16-MAC -> truncated 255 UTF8-MAC
+
+   Recent Mac OS X (10.4.?-) don't have this limitation.
+   Desirable implementation is:
+      volcharset -> truncated 510 UTF16-MAC -> UTF8-MAC
 
-/* -----------------------
+   ------------------------
    with utf8 filename not always round trip
    filename   mac filename too long or first chars if unmatchable chars.
    uname      unix filename 
    id         file/folder ID or 0
-   
 */
 char *
 mangle(const struct vol *vol, char *filename, size_t filenamelen, char *uname, cnid_t id, int flags) {
@@ -240,7 +250,7 @@ mangle(const struct vol *vol, char *filename, size_t filenamelen, char *uname, c
     size_t maxlen;
     int k;
     
-    maxlen = (flags & 2)?255:MACFILELEN; /* was vol->max_filename */
+    maxlen = (flags & 2)?UTF8FILELEN_EARLY:MACFILELEN; /* was vol->max_filename */
     /* Do we really need to mangle this filename? */
     if (!(flags & 1) && filenamelen <= maxlen) {
        return filename;
index 2812886229b7d5e96688012bf55a117f62d81c8f..13e03f4066cb7fc6cca11fb0ae588846a6fde615 100644 (file)
@@ -89,7 +89,7 @@ void readmessage(AFPObj *obj)
                                strerror(errno));
         }
 
-        if ( 0 < (rc = unlink(filename)) )
+        if ((rc = unlink(filename)) != 0)
            LOG(log_error, logtype_afpd, "File '%s' could not be deleted", strerror(errno));
 
         /* Drop privs again, failing this is very bad */
index 5d3c48bc05c6479724788f324040e1c0448ec19e..ed5f732814e7dd5d35ad2aa088c319a2f3d3d8e6 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: ofork.c,v 1.32 2010-03-12 15:16:49 franklahm Exp $
- *
  * Copyright (c) 1996 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
  */
 #include <string.h>
 #include <sys/stat.h> /* works around a bug */
 #include <sys/param.h>
-#include <atalk/logger.h>
 #include <errno.h>
 
+#include <atalk/logger.h>
 #include <atalk/util.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
 
 #include "globals.h"
 #include "volume.h"
@@ -33,9 +33,9 @@
 #define OFORK_HASHSIZE  64
 static struct ofork     *ofork_table[OFORK_HASHSIZE];
 
-static struct ofork    **oforks = NULL;
-static int             nforks = 0;
-static u_short         lastrefnum = 0;
+static struct ofork **oforks = NULL;
+static int          nforks = 0;
+static u_short      lastrefnum = 0;
 
 
 /* OR some of each character for the hash*/
@@ -46,7 +46,7 @@ static unsigned long hashfn(const struct file_key *key)
     while (*name) {
         i = ((i << 4) | (8*sizeof(i) - 4)) ^ *name++;
     }
-#endif    
+#endif
     return key->inode & (OFORK_HASHSIZE - 1);
 }
 
@@ -73,7 +73,7 @@ static void of_unhash(struct ofork *of)
 #ifdef DEBUG1
 void of_pforkdesc( FILE *f)
 {
-    int        ofrefnum;
+    int ofrefnum;
 
     if (!oforks)
         return;
@@ -88,14 +88,14 @@ void of_pforkdesc( FILE *f)
 
 int of_flush(const struct vol *vol)
 {
-    int        refnum;
+    int refnum;
 
     if (!oforks)
         return 0;
 
     for ( refnum = 0; refnum < nforks; refnum++ ) {
         if (oforks[ refnum ] != NULL && (oforks[refnum]->of_vol == vol) &&
-                flushfork( oforks[ refnum ] ) < 0 ) {
+            flushfork( oforks[ refnum ] ) < 0 ) {
             LOG(log_error, logtype_afpd, "of_flush: %s", strerror(errno) );
         }
     }
@@ -107,65 +107,51 @@ int of_rename(const struct vol *vol,
               struct dir *olddir, const char *oldpath _U_,
               struct dir *newdir, const char *newpath)
 {
-    struct ofork *of, *next, *d_ofork;
+    struct ofork *of, *next;
     int done = 0;
 
     if (!s_of)
         return AFP_OK;
-        
+
     next = ofork_table[hashfn(&s_of->key)];
     while ((of = next)) {
         next = next->next; /* so we can unhash and still be all right. */
 
-        if (vol == of->of_vol && olddir == of->of_dir &&
-                s_of->key.dev == of->key.dev && 
-                s_of->key.inode == of->key.inode ) {
-           if (!done) {
-               strlcpy( of_name(of), newpath, of->of_ad->ad_m_namelen);
-               done = 1;
-           }
-            if (newdir != olddir) {
-                of->of_d_prev->of_d_next = of->of_d_next;
-                of->of_d_next->of_d_prev = of->of_d_prev;
-                if (of->of_dir->d_ofork == of) {
-                    of->of_dir->d_ofork = (of == of->of_d_next) ? NULL : of->of_d_next;
-                }          
-                of->of_dir = newdir;
-                if (!(d_ofork = newdir->d_ofork)) {
-                    newdir->d_ofork = of;
-                    of->of_d_next = of->of_d_prev = of;
-                } else {
-                    of->of_d_next = d_ofork;
-                    of->of_d_prev = d_ofork->of_d_prev;
-                    of->of_d_prev->of_d_next = of;
-                    d_ofork->of_d_prev = of;
-                }
+        if (vol == of->of_vol
+            && olddir->d_did == of->of_did
+            && s_of->key.dev == of->key.dev
+            && s_of->key.inode == of->key.inode ) {
+            if (!done) {
+                strlcpy( of_name(of), newpath, of->of_ad->ad_m_namelen);
+                done = 1;
             }
+            if (newdir != olddir)
+                of->of_did = newdir->d_did;
         }
     }
 
     return AFP_OK;
 }
 
-#define min(a,b)       ((a)<(b)?(a):(b))
+#define min(a,b)    ((a)<(b)?(a):(b))
 
 struct ofork *
 of_alloc(struct vol *vol,
-    struct dir    *dir,
-    char          *path,
-    u_int16_t     *ofrefnum,
-    const int      eid,
-    struct adouble *ad,
-    struct stat    *st)
+         struct dir    *dir,
+         char      *path,
+         u_int16_t     *ofrefnum,
+         const int      eid,
+         struct adouble *ad,
+         struct stat    *st)
 {
-    struct ofork        *of, *d_ofork;
-    u_int16_t          refnum, of_refnum;
+    struct ofork        *of;
+    u_int16_t       refnum, of_refnum;
 
-    int                        i;
+    int         i;
 
     if (!oforks) {
         nforks = getdtablesize() - 10;
-       /* protect against insane ulimit -n */
+        /* protect against insane ulimit -n */
         nforks = min(nforks, 0xffff);
         oforks = (struct ofork **) calloc(nforks, sizeof(struct ofork *));
         if (!oforks)
@@ -181,22 +167,22 @@ of_alloc(struct vol *vol,
         }
     }
     /* grr, Apple and their 'uniquely identifies'
-          the next line is a protection against 
-          of_alloc()
-             refnum % nforks = 3 
-             lastrefnum = 3
-             oforks[3] != NULL 
-             refnum = 4
-             oforks[4] == NULL
-             return 4
-         
-          close(oforks[4])
-      
-          of_alloc()
-             refnum % nforks = 4
-             ...
-             return 4
-         same if lastrefnum++ rather than ++lastrefnum. 
+       the next line is a protection against
+       of_alloc()
+       refnum % nforks = 3
+       lastrefnum = 3
+       oforks[3] != NULL
+       refnum = 4
+       oforks[4] == NULL
+       return 4
+
+       close(oforks[4])
+
+       of_alloc()
+       refnum % nforks = 4
+       ...
+       return 4
+       same if lastrefnum++ rather than ++lastrefnum.
     */
     lastrefnum = refnum;
     if ( i == nforks ) {
@@ -206,7 +192,7 @@ of_alloc(struct vol *vol,
 
     of_refnum = refnum % nforks;
     if (( oforks[ of_refnum ] =
-                (struct ofork *)malloc( sizeof( struct ofork ))) == NULL ) {
+          (struct ofork *)malloc( sizeof( struct ofork ))) == NULL ) {
         LOG(log_error, logtype_afpd, "of_alloc: malloc: %s", strerror(errno) );
         return NULL;
     }
@@ -226,7 +212,7 @@ of_alloc(struct vol *vol,
            ad_open really does reinitialize the structure. */
         ad_init(ad, vol->v_adouble, vol->v_ad_options);
 
-        ad->ad_m_namelen = 255 +1;
+        ad->ad_m_namelen = UTF8FILELEN_EARLY +1;
         /* here's the deal: we allocate enough for the standard mac file length.
          * in the future, we'll reallocate in fairly large jumps in case
          * of long unicode names */
@@ -246,17 +232,7 @@ of_alloc(struct vol *vol,
 
     of->of_ad = ad;
     of->of_vol = vol;
-    of->of_dir = dir;
-
-    if (!(d_ofork = dir->d_ofork)) {
-        dir->d_ofork = of;
-        of->of_d_next = of->of_d_prev = of;
-    } else {
-        of->of_d_next = d_ofork;
-        of->of_d_prev = d_ofork->of_d_prev;
-        d_ofork->of_d_prev->of_d_next = of;
-        d_ofork->of_d_prev = of;
-    }
+    of->of_did = dir->d_did;
 
     *ofrefnum = refnum;
     of->of_refnum = refnum;
@@ -280,17 +256,24 @@ struct ofork *of_find(const u_int16_t ofrefnum )
 }
 
 /* -------------------------- */
-int of_stat  (struct path *path)
+int of_stat(struct path *path)
 {
-int ret;
+    int ret;
+
     path->st_errno = 0;
     path->st_valid = 1;
-    if ((ret = lstat(path->u_name, &path->st)) < 0)
+
+    if ((ret = lstat(path->u_name, &path->st)) < 0) {
+        LOG(log_debug, logtype_afpd, "of_stat('%s/%s': %s)",
+            cfrombstr(curdir->d_fullpath), path->u_name, strerror(errno));
        path->st_errno = errno;
-   return ret;
+    }
+
+    return ret;
 }
 
-#ifdef HAVE_RENAMEAT
+
+#ifdef HAVE_ATFUNCS
 int of_fstatat(int dirfd, struct path *path)
 {
     int ret;
@@ -303,17 +286,19 @@ int of_fstatat(int dirfd, struct path *path)
 
    return ret;
 }
-#endif /* HAVE_RENAMEAT */
+#endif /* HAVE_ATFUNCS */
 
 /* -------------------------- 
    stat the current directory.
    stat(".") works even if "." is deleted thus
    we have to stat ../name because we want to know if it's there
 */
-int of_statdir  (struct vol *vol, struct path *path)
+int of_statdir(struct vol *vol, struct path *path)
 {
-static char pathname[ MAXPATHLEN + 1] = "../";
-int ret;
+    static char pathname[ MAXPATHLEN + 1] = "../";
+    int ret;
+    size_t len;
+    struct dir *dir;
 
     if (*path->m_name) {
         /* not curdir */
@@ -322,20 +307,28 @@ int ret;
     path->st_errno = 0;
     path->st_valid = 1;
     /* FIXME, what about: we don't have r-x perm anymore ? */
-    strlcpy(pathname +3, path->d_dir->d_u_name, sizeof (pathname) -3);
+    len = blength(path->d_dir->d_u_name);
+    if (len > (MAXPATHLEN - 3))
+        len = MAXPATHLEN - 3;
+    strncpy(pathname + 3, cfrombstr(path->d_dir->d_u_name), len + 1);
+
+    LOG(log_debug, logtype_afpd, "of_statdir: stating: '%s'", pathname);
 
     if (!(ret = lstat(pathname, &path->st)))
         return 0;
-        
+
     path->st_errno = errno;
+
     /* hmm, can't stat curdir anymore */
-    if (errno == EACCES && curdir->d_parent ) {
-       if (movecwd(vol, curdir->d_parent)) 
+    if (errno == EACCES && (dir = dirlookup(vol, curdir->d_pdid))) {
+       if (movecwd(vol, dir)) 
            return -1;
        path->st_errno = 0;
-       if ((ret = lstat(path->d_dir->d_u_name, &path->st)) < 0) 
+
+       if ((ret = lstat(cfrombstr(path->d_dir->d_u_name), &path->st)) < 0) 
            path->st_errno = errno;
     }
+
     return ret;
 }
 
@@ -344,11 +337,11 @@ struct ofork *of_findname(struct path *path)
 {
     struct ofork *of;
     struct file_key key;
-    
+
     if (!path->st_valid) {
-       of_stat(path);
+        of_stat(path);
     }
-       
+
     if (path->st_errno)
         return NULL;
 
@@ -373,7 +366,7 @@ struct ofork *of_findname(struct path *path)
  * @param dirfd     (r) directory fd
  * @param path      (rw) pointer to struct path
  */
-#ifdef HAVE_RENAMEAT
+#ifdef HAVE_ATFUNCS
 struct ofork *of_findnameat(int dirfd, struct path *path)
 {
     struct ofork *of;
@@ -405,14 +398,6 @@ void of_dealloc( struct ofork *of)
         return;
 
     of_unhash(of);
-
-    /* detach ofork */
-    of->of_d_prev->of_d_next = of->of_d_next;
-    of->of_d_next->of_d_prev = of->of_d_prev;
-    if (of->of_dir->d_ofork == of) {
-        of->of_dir->d_ofork = (of == of->of_d_next) ? NULL : of->of_d_next;
-    }
-
     oforks[ of->of_refnum % nforks ] = NULL;
 
     /* decrease refcount */
@@ -432,12 +417,12 @@ void of_dealloc( struct ofork *of)
 int of_closefork(struct ofork *ofork)
 {
     struct timeval      tv;
-    int                        adflags, doflush = 0;
+    int         adflags, doflush = 0;
     int                 ret;
 
     adflags = 0;
     if ((ofork->of_flags & AFPFORK_DATA) && (ad_data_fileno( ofork->of_ad ) != -1)) {
-            adflags |= ADFLAGS_DF;
+        adflags |= ADFLAGS_DF;
     }
     if ( (ofork->of_flags & AFPFORK_OPEN) && ad_reso_fileno( ofork->of_ad ) != -1 ) {
         adflags |= ADFLAGS_HF;
@@ -448,10 +433,10 @@ int of_closefork(struct ofork *ofork)
             ad_refresh( ofork->of_ad );
             if ((ofork->of_flags & AFPFORK_DIRTY) && !gettimeofday(&tv, NULL)) {
                 ad_setdate(ofork->of_ad, AD_DATE_MODIFY | AD_DATE_UNIX,tv.tv_sec);
-               doflush++;
+                doflush++;
             }
             if ( doflush ) {
-                 ad_flush( ofork->of_ad );
+                ad_flush( ofork->of_ad );
             }
         }
     }
@@ -465,14 +450,14 @@ int of_closefork(struct ofork *ofork)
     if ( ad_close( ofork->of_ad, adflags ) < 0 ) {
         ret = -1;
     }
+
     of_dealloc( ofork );
     return ret;
 }
 
 /* ----------------------
 
-*/
+ */
 struct adouble *of_ad(const struct vol *vol, struct path *path, struct adouble *ad)
 {
     struct ofork        *of;
@@ -487,12 +472,12 @@ struct adouble *of_ad(const struct vol *vol, struct path *path, struct adouble *
     return adp;
 }
 
-/* ---------------------- 
+/* ----------------------
    close all forks for a volume
 */
 void of_closevol(const struct vol *vol)
 {
-    int        refnum;
+    int refnum;
 
     if (!oforks)
         return;
@@ -507,3 +492,23 @@ void of_closevol(const struct vol *vol)
     return;
 }
 
+/* ----------------------
+   close all forks for a volume
+*/
+void of_close_all_forks(void)
+{
+    int refnum;
+
+    if (!oforks)
+        return;
+
+    for ( refnum = 0; refnum < nforks; refnum++ ) {
+        if (oforks[ refnum ] != NULL) {
+            if (of_closefork( oforks[ refnum ]) < 0 ) {
+                LOG(log_error, logtype_afpd, "of_close_all_forks: %s", strerror(errno) );
+            }
+        }
+    }
+    return;
+}
+
index bcdca483f38c5d5e3b6ca54fe3e4a533d7c62907..cbf59892c157143fb74d9fef60b0de4f12a04bb1 100644 (file)
@@ -35,6 +35,7 @@
 #include <atalk/asp.h>
 #include <atalk/nbp.h>
 #include <atalk/unicode.h>
+#include <atalk/util.h>
 
 #include "globals.h"  /* includes <netdb.h> */
 #include "status.h"
@@ -46,32 +47,26 @@ static   size_t maxstatuslen = 0;
 static void status_flags(char *data, const int notif, const int ipok,
                          const unsigned char passwdbits, const int dirsrvcs _U_, int flags)
 {
-    u_int16_t           status;
+    uint16_t           status;
+
+    status = AFPSRVRINFO_COPY
+           | AFPSRVRINFO_SRVSIGNATURE
+           | AFPSRVRINFO_SRVMSGS
+           | AFPSRVRINFO_FASTBOZO
+           | AFPSRVRINFO_SRVRDIR
+           | AFPSRVRINFO_SRVUTF8
+           | AFPSRVRINFO_EXTSLEEP;
 
-    status = AFPSRVRINFO_COPY;
     if (passwdbits & PASSWD_SET) /* some uams may not allow this. */
         status |= AFPSRVRINFO_PASSWD;
     if (passwdbits & PASSWD_NOSAVE)
         status |= AFPSRVRINFO_NOSAVEPASSWD;
-    status |= AFPSRVRINFO_SRVSIGNATURE;
-    /* only advertise tcp/ip if we have a valid address */
-    if (ipok)
+    if (ipok) /* only advertise tcp/ip if we have a valid address */        
         status |= AFPSRVRINFO_TCPIP;
-    status |= AFPSRVRINFO_SRVMSGS;
-    /* Allow the user to decide if we should support server notifications.
-     * With this turned off, the clients will poll for directory changes every
-     * 10 seconds.  This might be too costly to network resources, so make
-     * this an optional thing.  Default will be to _not_ support server
-     * notifications. */
-    if (notif) {
+    if (notif) /* Default is yes */        
         status |= AFPSRVRINFO_SRVNOTIFY;
-    }
-    status |= AFPSRVRINFO_FASTBOZO;
-    status |= AFPSRVRINFO_SRVRDIR; /* AFP 3.1 specs says we need to specify this, but may set the count to 0 */
-    /* We don't set the UTF8 name flag here, we don't know whether we have enough space ... */
-
-    if (flags & OPTION_UUID)   /* 05122008 FIXME: can we set AFPSRVRINFO_UUID here ? see AFPSRVRINFO_SRVRDIR*/
-       status |= AFPSRVRINFO_UUID;
+    if (flags & OPTION_UUID)
+        status |= AFPSRVRINFO_UUID;
 
     status = htons(status);
     memcpy(data + AFPSTATUS_FLAGOFF, &status, sizeof(status));
@@ -133,15 +128,19 @@ static void status_machine(char *data)
 #ifdef AFS
     const char         *machine = "afs";
 #else /* !AFS */
-    const char         *machine = "Netatalk";
+    const char         *machine = "Netatalk %s";
 #endif /* AFS */
+    char buf[64];
 
     memcpy(&status, start + AFPSTATUS_MACHOFF, sizeof(status));
     data += ntohs( status );
-    len = strlen( machine );
+
+    //    len = strlen( machine );
+    len = snprintf(buf, 64, machine, VERSION);
     *data++ = len;
-    memcpy( data, machine, len );
+    memcpy( data, buf, len );
     data += len;
+
     status = htons(data - start);
     memcpy(start + AFPSTATUS_VERSOFF, &status, sizeof(status));
 }
@@ -393,13 +392,6 @@ static size_t status_utf8servername(char *data, int *nameoffset,
        data += len;
        offset = htons(offset);
        memcpy(begin + *nameoffset, &offset, sizeof(u_int16_t));
-        
-        /* Now set the flag ... */
-       memcpy(&status, begin + AFPSTATUS_FLAGOFF, sizeof(status));
-       status = ntohs(status);
-       status |= AFPSRVRINFO_SRVUTF8;
-       status = htons(status);
-       memcpy(begin + AFPSTATUS_FLAGOFF, &status, sizeof(status));
     }
 
     /* return length of buffer */
@@ -556,7 +548,7 @@ void set_signature(struct afp_options *options) {
     char *servername_conf;
     int header = 0;
     char buf[1024], *p;
-    FILE *fp, *randomp;
+    FILE *fp = NULL, *randomp;
     size_t len;
     char *server_tmp;
     
@@ -661,12 +653,12 @@ server_signature_auto:
         }
     } else {                                                          /* conf file don't exist */
         if (( fd = creat(options->sigconffile, 0644 )) < 0 ) {
-            LOG(log_error, logtype_atalkd, "ERROR: Cannot create %s (%s). Using one-time signature.",
+            LOG(log_error, logtype_afpd, "ERROR: Cannot create %s (%s). Using one-time signature.",
                 options->sigconffile, strerror(errno));
             goto server_signature_random;
         }
         if (( fp = fdopen( fd, "w" )) == NULL ) {
-            LOG(log_error, logtype_atalkd, "ERROR: Cannot fdopen %s (%s). Using one-time signature.",
+            LOG(log_error, logtype_afpd, "ERROR: Cannot fdopen %s (%s). Using one-time signature.",
                 options->sigconffile, strerror(errno));
             close(fd);
             goto server_signature_random;
@@ -678,41 +670,9 @@ server_signature_auto:
 server_signature_random:
     
     /* generate signature from random number */
-    if ((randomp = fopen("/dev/urandom", "r")) != NULL) {   /* generate from /dev/urandom */
-        for (i=0 ; i<16 ; i++) {
-            (options->signature)[i] = fgetc(randomp);
-        }
-        LOG(log_note, logtype_afpd,
-            "generate %s's signature %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X from /dev/urandom",
-            server_tmp,
-            (options->signature)[ 0], (options->signature)[ 1],
-            (options->signature)[ 2], (options->signature)[ 3],
-            (options->signature)[ 4], (options->signature)[ 5],
-            (options->signature)[ 6], (options->signature)[ 7],
-            (options->signature)[ 8], (options->signature)[ 9],
-            (options->signature)[10], (options->signature)[11],
-            (options->signature)[12], (options->signature)[13],
-            (options->signature)[14], (options->signature)[15]);
-        
-    } else {                                   /* genarate from random() because cannot open /dev/urandom */
-        srandom((unsigned int)time(NULL) + (unsigned int)options + (unsigned int)server_tmp);
-        for (i=0 ; i<16 ; i++) {
-            (options->signature)[i] = random() & 0xFF;
-        }
-        LOG(log_note, logtype_afpd,
-            "generate %s's signature %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X from random()",
-            server_tmp,
-            (options->signature)[ 0], (options->signature)[ 1],
-            (options->signature)[ 2], (options->signature)[ 3],
-            (options->signature)[ 4], (options->signature)[ 5],
-            (options->signature)[ 6], (options->signature)[ 7],
-            (options->signature)[ 8], (options->signature)[ 9],
-            (options->signature)[10], (options->signature)[11],
-            (options->signature)[12], (options->signature)[13],
-            (options->signature)[14], (options->signature)[15]);
-    }
+    randombytes(options->signature, 16);
 
-    if (fp && header) {                                     /* conf file is created or size=0 */
+    if (fp && header) { /* conf file is created or size=0 */
         fprintf(fp, "# DON'T TOUCH NOR COPY THOUGHTLESSLY!\n");
         fprintf(fp, "# This file is auto-generated by afpd.\n");
         fprintf(fp, "# \n");
index 2e2c69e01212f56e25df986d83c46e478c42ad55..7dbd9ab77315f57eba5a8c4aa0d1c8bc0d360472 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: switch.c,v 1.19 2009-10-15 10:43:13 didg Exp $
- *
  * Copyright (c) 1990,1991 Regents of The University of Michigan.
  * All Rights Reserved.
  *
@@ -46,7 +44,7 @@
 #include "filedir.h"
 #include "status.h"
 #include "misc.h"
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
 #include "acls.h"
 #endif
 
index 3bb636f7ece899a0b0a3846b9fad32d92fb47673..92173915c5f5cc5e7dfa8934c00f1a1ba34a1d85 100644 (file)
@@ -56,11 +56,7 @@ char *strchr (), *strrchr ();
 #include "auth.h"
 #include "uam_auth.h"
 
-#ifdef AFP3x
 #define utf8_encoding() (afp_version >= 30)
-#else
-#define utf8_encoding() (0)
-#endif
 
 #ifdef TRU64
 #include <netdb.h>
@@ -428,7 +424,7 @@ int uam_afpserver_option(void *private, const int what, void *option,
     {
         struct DSI *dsi = obj->handle;
         const struct sockaddr *sa;
-        char hbuf[NI_MAXHOST];
+        static char hbuf[NI_MAXHOST];
         
         sa = (struct sockaddr *)&dsi->client;
         if (getnameinfo(sa, sizeof(dsi->client), hbuf, sizeof(hbuf), NULL, 0, 0) == 0)
index 57491a7184a200f17b48a2e5a1f9d9959e84f958..f604d79b8258a44ccdb5b228d18c543f40bda77c 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: unix.c,v 1.61 2010-02-10 14:05:37 franklahm Exp $
- *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
  */
@@ -38,18 +36,17 @@ char *strchr (), *strrchr ();
 #include <atalk/afp.h>
 #include <atalk/util.h>
 #include <atalk/unix.h>
+#include <atalk/acl.h>
 
 #include "auth.h"
 #include "directory.h"
 #include "volume.h"
 #include "unix.h"
 #include "fork.h"
-
-#ifdef HAVE_NFSv4_ACLS
-extern void acltoownermode(char *path, struct stat *st,uid_t uid, struct maccess *ma);
+#ifdef HAVE_ACLS
+#include "acls.h"
 #endif
 
-
 /*
  * Get the free space on a partition.
  */
@@ -160,6 +157,7 @@ mode_t mode;
 }
 
 #ifdef accessmode
+
 #undef accessmode
 #endif
 /*
@@ -172,9 +170,8 @@ mode_t mode;
  * dir parameter is used by AFS
  */
 void accessmode(char *path, struct maccess *ma, struct dir *dir _U_, struct stat *st)
-
 {
-struct stat     sb;
+    struct stat     sb;
 
     ma->ma_user = ma->ma_owner = ma->ma_world = ma->ma_group = 0;
     if (!st) {
@@ -183,9 +180,8 @@ struct stat     sb;
         st = &sb;
     }
     utommode( st, ma );
-#ifdef HAVE_NFSv4_ACLS
-    /* 10.5 Finder looks at OS 9 mode, so we must do some mapping */
-    acltoownermode( path, st, uuid, ma);
+#ifdef HAVE_ACLS
+    acltoownermode(path, st, ma);
 #endif
 }
 
index 9a5a4dc771e036443c694b2d3428b8d50843261d..b73d13c239ece87e8494c127dde753bc8ea7c235 100644 (file)
@@ -35,6 +35,7 @@ char *strchr (), *strrchr ();
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+
 #include <atalk/asp.h>
 #include <atalk/dsi.h>
 #include <atalk/adouble.h>
@@ -43,7 +44,11 @@ char *strchr (), *strrchr ();
 #include <atalk/volinfo.h>
 #include <atalk/logger.h>
 #include <atalk/vfs.h>
-#include <atalk/ea.h>
+#include <atalk/uuid.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
+
+
 #ifdef CNID_DB
 #include <atalk/cnid.h>
 #endif /* CNID_DB*/
@@ -56,6 +61,7 @@ char *strchr (), *strrchr ();
 #include "mangle.h"
 #include "fork.h"
 #include "hash.h"
+#include "acls.h"
 
 extern int afprun(int root, char *cmd, int *outfd);
 
@@ -74,6 +80,13 @@ extern int afprun(int root, char *cmd, int *outfd);
 #endif /* BYTE_ORDER == BIG_ENDIAN */
 #endif /* ! NO_LARGE_VOL_SUPPORT */
 
+#ifndef UUID_PRINTABLE_STRING_LENGTH
+#define UUID_PRINTABLE_STRING_LENGTH 37
+#endif
+
+/* Globals */
+struct vol *current_vol;        /* last volume from getvolbyvid() */
+
 static struct vol *Volumes = NULL;
 static u_int16_t    lastvid = 0;
 static char     *Trash = "\02\024Network Trash Folder";
@@ -154,6 +167,8 @@ static void handle_special_folders (const struct vol *);
 static void deletevol(struct vol *vol);
 static void volume_free(struct vol *vol);
 static void check_ea_sys_support(struct vol *vol);
+static char *get_vol_uuid(const AFPObj *obj, const char *volname);
+static int readvolfile(AFPObj *obj, struct afp_volume_name *p1,char *p2, int user, struct passwd *pwent);
 
 static void volfree(struct vol_option *options, const struct vol_option *save)
 {
@@ -173,7 +188,10 @@ static void volfree(struct vol_option *options, const struct vol_option *save)
 }
 
 
-/* handle variable substitutions. here's what we understand:
+#define is_var(a, b) (strncmp((a), (b), 2) == 0)
+
+/*
+ * Handle variable substitutions. here's what we understand:
  * $b   -> basename of path
  * $c   -> client ip/appletalk address
  * $d   -> volume pathname on server
@@ -187,17 +205,37 @@ static void volfree(struct vol_option *options, const struct vol_option *save)
  * $z   -> zone (may not exist)
  * $$   -> $
  *
+ * This get's called from readvolfile with
+ * path = NULL, volname = NULL for xlating the volumes path
+ * path = path, volname = NULL for xlating the volumes name
+ * ... and from volumes options parsing code when xlating eg dbpath with
+ * path = path, volname = volname
  *
+ * Using this information we can reject xlation of any variable depeninding on a login
+ * context which is not given in the afp master, where we must evaluate this whole stuff
+ * too for the Zeroconf announcements.
  */
-#define is_var(a, b) (strncmp((a), (b), 2) == 0)
-
-static char *volxlate(AFPObj *obj, char *dest, size_t destlen,
-                      char *src, struct passwd *pwd, char *path, char *volname)
+static char *volxlate(AFPObj *obj,
+                      char *dest,
+                      size_t destlen,
+                      char *src,
+                      struct passwd *pwd,
+                      char *path,
+                      char *volname)
 {
     char *p, *r;
     const char *q;
     int len;
     char *ret;
+    int afpmaster = 0;
+    int xlatevolname = 0;
+
+    if (parent_or_child == 0)
+        afpmaster = 1;
+
+    if (path && !volname)
+        /* cf above */
+        xlatevolname = 1;
 
     if (!src) {
         return NULL;
@@ -224,6 +262,8 @@ static char *volxlate(AFPObj *obj, char *dest, size_t destlen,
         /* now figure out what the variable is */
         q = NULL;
         if (is_var(p, "$b")) {
+            if (afpmaster && xlatevolname)
+                return NULL;
             if (path) {
                 if ((q = strrchr(path, '/')) == NULL)
                     q = path;
@@ -231,6 +271,8 @@ static char *volxlate(AFPObj *obj, char *dest, size_t destlen,
                     q++;
             }
         } else if (is_var(p, "$c")) {
+            if (afpmaster && xlatevolname)
+                return NULL;
             if (obj->proto == AFPPROTO_ASP) {
                 ASP asp = obj->handle;
 
@@ -248,18 +290,26 @@ static char *volxlate(AFPObj *obj, char *dest, size_t destlen,
                 destlen -= len;
             }
         } else if (is_var(p, "$d")) {
+            if (afpmaster && xlatevolname)
+                return NULL;
             q = path;
-        } else if (is_var(p, "$f")) {
+        } else if (pwd && is_var(p, "$f")) {
+            if (afpmaster && xlatevolname)
+                return NULL;
             if ((r = strchr(pwd->pw_gecos, ',')))
                 *r = '\0';
             q = pwd->pw_gecos;
-        } else if (is_var(p, "$g")) {
+        } else if (pwd && is_var(p, "$g")) {
+            if (afpmaster && xlatevolname)
+                return NULL;
             struct group *grp = getgrgid(pwd->pw_gid);
             if (grp)
                 q = grp->gr_name;
         } else if (is_var(p, "$h")) {
             q = obj->options.hostname;
         } else if (is_var(p, "$i")) {
+            if (afpmaster && xlatevolname)
+                return NULL;
             if (obj->proto == AFPPROTO_ASP) {
                 ASP asp = obj->handle;
 
@@ -278,13 +328,17 @@ static char *volxlate(AFPObj *obj, char *dest, size_t destlen,
                 q = obj->options.server;
             } else
                 q = obj->options.hostname;
-        } else if (is_var(p, "$u")) {
+        } else if (obj->username && is_var(p, "$u")) {
+            if (afpmaster && xlatevolname)
+                return NULL;
             char* sep = NULL;
             if ( obj->options.ntseparator && (sep = strchr(obj->username, obj->options.ntseparator[0])) != NULL)
                 q = sep+1;
             else
                 q = obj->username;
         } else if (is_var(p, "$v")) {
+            if (afpmaster && xlatevolname)
+                return NULL;
             if (volname) {
                 q = volname;
             }
@@ -445,8 +499,6 @@ static void volset(struct vol_option *options, struct vol_option *save,
                 options[VOLOPT_ROOTPREEXEC].i_value = 1;
             else if (strcasecmp(p, "upriv") == 0)
                 options[VOLOPT_FLAGS].i_value |= AFPVOL_UNIX_PRIV;
-            else if (strcasecmp(p, "acls") == 0)
-                options[VOLOPT_FLAGS].i_value |= AFPVOL_ACLS;
             else if (strcasecmp(p, "nodev") == 0)
                 options[VOLOPT_FLAGS].i_value |= AFPVOL_NODEV;
             else if (strcasecmp(p, "caseinsensitive") == 0)
@@ -457,7 +509,13 @@ static void volset(struct vol_option *options, struct vol_option *save,
                 options[VOLOPT_FLAGS].i_value &= ~AFPVOL_CACHE;
             else if (strcasecmp(p, "tm") == 0)
                 options[VOLOPT_FLAGS].i_value |= AFPVOL_TM;
-
+            else if (strcasecmp(p, "searchdb") == 0)
+                options[VOLOPT_FLAGS].i_value |= AFPVOL_SEARCHDB;
+/* Found this in branch dir-rewrite, maybe we want to use it sometimes */
+#if 0
+            else if (strcasecmp(p, "cdrom") == 0)
+                options[VOLOPT_FLAGS].i_value |= AFPVOL_CDROM | AFPVOL_RO;
+#endif
             p = strtok(NULL, ",");
         }
 
@@ -559,6 +617,8 @@ static int creatvol(AFPObj *obj, struct passwd *pwd,
     char        suffix[6]; /* max is #FFFF */
     u_int16_t   flags;
 
+    LOG(log_debug, logtype_afpd, "createvol: Volume '%s'", name);
+
     if ( name == NULL || *name == '\0' ) {
         if ((name = strrchr( path, '/' )) == NULL) {
             return -1;  /* Obviously not a fully qualified path */
@@ -599,7 +659,7 @@ static int creatvol(AFPObj *obj, struct passwd *pwd,
     if ( 0 >= ( u8mvlen = convert_string(CH_UTF8_MAC, CH_UCS2, tmpname, tmpvlen, u8mtmpname, AFPVOL_U8MNAMELEN*2)) )
         return -1;
 
-    LOG(log_debug, logtype_afpd, "createvol: Volume '%s' -> UTF8-MAC Name: '%s'", name, tmpname);
+    LOG(log_maxdebug, logtype_afpd, "createvol: Volume '%s' -> UTF8-MAC Name: '%s'", name, tmpname);
 
     /* Maccharset Volume Name */
     /* Firsty convert name from unixcharset to maccharset */
@@ -625,11 +685,12 @@ static int creatvol(AFPObj *obj, struct passwd *pwd,
     if ( 0 >= ( macvlen = convert_string(obj->options.maccharset, CH_UCS2, tmpname, tmpvlen, mactmpname, AFPVOL_U8MNAMELEN*2)) )
         return -1;
 
-    LOG(log_debug, logtype_afpd, "createvol: Volume '%s' ->  Longname: '%s'", name, tmpname);
+    LOG(log_maxdebug, logtype_afpd, "createvol: Volume '%s' ->  Longname: '%s'", name, tmpname);
 
     /* check duplicate */
     for ( volume = Volumes; volume; volume = volume->v_next ) {
         if (( strcasecmp_w( volume->v_u8mname, u8mtmpname ) == 0 ) || ( strcasecmp_w( volume->v_macname, mactmpname ) == 0 )){
+            LOG (log_error, logtype_afpd, "ERROR: Volume name is duplicated. Check AppleVolumes files.");
             if (volume->v_deleted) {
                 volume->v_new = hide = 1;
             }
@@ -678,14 +739,16 @@ static int creatvol(AFPObj *obj, struct passwd *pwd,
     /* os X start at 1 and use network order ie. 1 2 3 */
     volume->v_vid = ++lastvid;
     volume->v_vid = htons(volume->v_vid);
+#ifdef HAVE_ACLS
+    if (check_vol_acl_support(volume))
+        volume->v_flags |= AFPVOL_ACLS
+;
+#endif
 
     /* handle options */
     if (options) {
-        /* should we casefold? */
         volume->v_casefold = options[VOLOPT_CASEFOLD].i_value;
-
-        /* shift in some flags */
-        volume->v_flags = options[VOLOPT_FLAGS].i_value;
+        volume->v_flags |= options[VOLOPT_FLAGS].i_value;
 
         if (options[VOLOPT_EA_VFS].i_value)
             volume->v_vfs_ea = options[VOLOPT_EA_VFS].i_value;
@@ -807,6 +870,19 @@ static int creatvol(AFPObj *obj, struct passwd *pwd,
         check_ea_sys_support(volume);
     initvol_vfs(volume);
 
+    /* get/store uuid from file */
+    if (volume->v_flags & AFPVOL_TM) {
+        char *uuid = get_vol_uuid(obj, volume->v_localname);
+        if (!uuid) {
+            LOG(log_error, logtype_afpd, "Volume '%s': couldn't get UUID",
+                volume->v_localname);
+        } else {
+            volume->v_uuid = uuid;
+            LOG(log_debug, logtype_afpd, "Volume '%s': UUID '%s'",
+                volume->v_localname, volume->v_uuid);
+        }
+    }
+
     volume->v_next = Volumes;
     Volumes = volume;
     return 0;
@@ -1074,7 +1150,10 @@ static int volfile_changed(struct afp_volume_name *p)
 
 /* ----------------------
  * Read a volume configuration file and add the volumes contained within to
- * the global volume list.  If p2 is non-NULL, the file that is opened is
+ * the global volume list. This gets called from the forked afpd childs.
+ * The master now reads this too for Zeroconf announcements.
+ *
+ * If p2 is non-NULL, the file that is opened is
  * p1/p2
  *
  * Lines that begin with # and blank lines are ignored.
@@ -1082,6 +1161,7 @@ static int volfile_changed(struct afp_volume_name *p)
  *      <unix path> [<volume name>] [allow:<user>,<@group>,...] \
  *                           [codepage:<file>] [casefold:<num>]
  *      <extension> TYPE [CREATOR]
+ *
  */
 static int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int user, struct passwd *pwent)
 {
@@ -1092,12 +1172,12 @@ static int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int us
     char        buf[BUFSIZ];
     char        type[5], creator[5];
     char        *u, *p;
+    int         fd;
+    int         i;
     struct passwd   *pw;
     struct vol_option   save_options[VOLOPT_NUM];
     struct vol_option   options[VOLOPT_NUM];
-    int                 i;
     struct stat         st;
-    int                 fd;
 
     if (!p1->name)
         return -1;
@@ -1120,6 +1200,14 @@ static int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int us
         p1->mtime = st.st_mtime;
     }
 
+    if ((read_lock(fd, 0, SEEK_SET, 0)) != 0) {
+        LOG(log_error, logtype_afpd, "readvolfile: can't lock volume file \"%s\"", path);
+        if ( fclose( fp ) != 0 ) {
+            LOG(log_error, logtype_afpd, "readvolfile: fclose: %s", strerror(errno) );
+        }
+        return -1;
+    }
+
     memset(save_options, 0, sizeof(save_options));
 
     /* Enable some default options for all volumes */
@@ -1129,6 +1217,8 @@ static int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int us
         obj->options.umask);
     save_options[VOLOPT_UMASK].i_value = obj->options.umask;
 
+    LOG(log_debug, logtype_afpd, "readvolfile: \"%s\"", path);
+
     while ( myfgets( buf, sizeof( buf ), fp ) != NULL ) {
         initline( strlen( buf ), buf );
         parseline( sizeof( path ) - 1, path );
@@ -1174,29 +1264,16 @@ static int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int us
             /* send path through variable substitution */
             if (*path != '~') /* need to copy path to tmp */
                 strcpy(tmp, path);
-            if (!pwent)
+            if (!pwent && obj->username)
                 pwent = getpwnam(obj->username);
-            volxlate(obj, path, sizeof(path) - 1, tmp, pwent, NULL, NULL);
+
+            if (volxlate(obj, path, sizeof(path) - 1, tmp, pwent, NULL, NULL) == NULL)
+                continue;
 
             /* this is sort of braindead. basically, i want to be
              * able to specify things in any order, but i don't want to
-             * re-write everything.
-             *
-             * currently we have options:
-             *   volname
-             *   codepage:x
-             *   casefold:x
-             *   allow:x,y,@z
-             *   deny:x,y,@z
-             *   rwlist:x,y,@z
-             *   rolist:x,y,@z
-             *   options:prodos,crlf,noadouble,ro...
-             *   dbpath:x
-             *   password:x
-             *   preexec:x
-             *
-             *   namemask:x,y,!z  (not implemented yet)
-             */
+             * re-write everything. */
+
             memcpy(options, save_options, sizeof(options));
             *volname = '\0';
 
@@ -1208,27 +1285,32 @@ static int readvolfile(AFPObj *obj, struct afp_volume_name *p1, char *p2, int us
                 volset(options, save_options, volname, sizeof(volname) - 1, tmp);
             }
 
-            /* check allow/deny lists:
+            /* check allow/deny lists (if not afpd master loading volumes for Zeroconf reg.):
                allow -> either no list (-1), or in list (1)
                deny -> either no list (-1), or not in list (0) */
-            if (accessvol(options[VOLOPT_ALLOW].c_value, obj->username) &&
-                (accessvol(options[VOLOPT_DENY].c_value, obj->username) < 1) &&
-                hostaccessvol(VOLOPT_ALLOWED_HOSTS, volname, options[VOLOPT_ALLOWED_HOSTS].c_value, obj) &&
-                (hostaccessvol(VOLOPT_DENIED_HOSTS, volname, options[VOLOPT_DENIED_HOSTS].c_value, obj) < 1)) {
+            if (parent_or_child == 0
+                ||
+                (accessvol(options[VOLOPT_ALLOW].c_value, obj->username) &&
+                 (accessvol(options[VOLOPT_DENY].c_value, obj->username) < 1) &&
+                 hostaccessvol(VOLOPT_ALLOWED_HOSTS, volname, options[VOLOPT_ALLOWED_HOSTS].c_value, obj) &&
+                 (hostaccessvol(VOLOPT_DENIED_HOSTS, volname, options[VOLOPT_DENIED_HOSTS].c_value, obj) < 1))) {
 
                 /* handle read-only behaviour. semantics:
                  * 1) neither the rolist nor the rwlist exist -> rw
                  * 2) rolist exists -> ro if user is in it.
                  * 3) rwlist exists -> ro unless user is in it. */
-                if (((options[VOLOPT_FLAGS].i_value & AFPVOL_RO) == 0) &&
-                    ((accessvol(options[VOLOPT_ROLIST].c_value,
-                                obj->username) == 1) ||
-                     !accessvol(options[VOLOPT_RWLIST].c_value,
-                                obj->username)))
+                if (parent_or_child == 1
+                    &&
+                    ((options[VOLOPT_FLAGS].i_value & AFPVOL_RO) == 0)
+                    &&
+                    ((accessvol(options[VOLOPT_ROLIST].c_value, obj->username) == 1) ||
+                     !accessvol(options[VOLOPT_RWLIST].c_value, obj->username)))
                     options[VOLOPT_FLAGS].i_value |= AFPVOL_RO;
 
                 /* do variable substitution for volname */
-                volxlate(obj, tmp, sizeof(tmp) - 1, volname, pwent, path, NULL);
+                if (volxlate(obj, tmp, sizeof(tmp) - 1, volname, pwent, path, NULL) == NULL)
+                    continue;
+
                 creatvol(obj, pwent, path, tmp, options, p2 != NULL);
             }
             volfree(options, save_options);
@@ -1274,6 +1356,8 @@ static void volume_free(struct vol *vol)
     free(vol->v_forceuid);
     free(vol->v_forcegid);
 #endif /* FORCE_UIDGID */
+    if (vol->v_uuid)
+        free(vol->v_uuid);
 }
 
 /* ------------------------------- */
@@ -1375,10 +1459,45 @@ static int getvolspace(struct vol *vol,
 
 getvolspace_done:
     if (vol->v_limitsize) {
-        /* FIXME: Free could be limit minus (total minus used), */
-        /* which will confuse the client less ? */
-        *xbfree = min(*xbfree, (vol->v_limitsize * 1024 * 1024));
+        bstring cmdstr;
+        if ((cmdstr = bformat("du -sh \"%s\" 2> /dev/null | cut -f1", vol->v_path)) == NULL)
+            return AFPERR_MISC;
+
+        FILE *cmd = popen(cfrombstr(cmdstr), "r");
+        bdestroy(cmdstr);
+        if (cmd == NULL)
+            return AFPERR_MISC;
+
+        char buf[100];
+        fgets(buf, 100, cmd);
+
+        if (pclose(cmd) == -1)
+            return AFPERR_MISC;
+
+        size_t multi = 0;
+        if (buf[strlen(buf) - 2] == 'G' || buf[strlen(buf) - 2] == 'g')
+            /* GB */
+            multi = 1024 * 1024 * 1024;
+        else if (buf[strlen(buf) - 2] == 'M' || buf[strlen(buf) - 2] == 'm')
+            /* MB */
+            multi = 1024 * 1024;
+        else if (buf[strlen(buf) - 2] == 'K' || buf[strlen(buf) - 2] == 'k')
+            /* MB */
+            multi = 1024;
+
+        char *p;
+        if (p = strchr(buf, ','))
+            /* ignore fraction */
+            *p = 0;
+        else
+            /* remove G|M|K char */
+            buf[strlen(buf) - 2] = 0;
+        /* now buf contains only digits */
+        long long used = atoll(buf) * multi;
+        LOG(log_debug, logtype_afpd, "volparams: used on volume: %llu bytes", used);
+
         *xbtotal = min(*xbtotal, (vol->v_limitsize * 1024 * 1024));
+        *xbfree = min(*xbfree, *xbtotal < used ? 0 : *xbtotal - used);
     }
 
     *bfree = min( *xbfree, maxsize);
@@ -1669,6 +1788,12 @@ void load_volumes(AFPObj *obj)
         free_volumes();
     }
 
+    if (parent_or_child == 0) {
+        LOG(log_debug, logtype_afpd, "load_volumes: AFP MASTER");
+    } else {
+        LOG(log_debug, logtype_afpd, "load_volumes: user: %s", obj->username);
+    }
+
     pwent = getpwnam(obj->username);
     if ( (obj->options.flags & OPTION_USERVOLFIRST) == 0 ) {
         readvolfile(obj, &obj->options.systemvol, NULL, 0, pwent);
@@ -1837,23 +1962,46 @@ static int volume_openDB(struct vol *volume)
             volume->v_path, volume->v_cnidscheme);
     }
 
-    LOG(log_info, logtype_afpd, "CNID server %s:%s",
+    LOG(log_info, logtype_afpd, "CNID server: %s:%s",
         volume->v_cnidserver ? volume->v_cnidserver : Cnid_srv,
         volume->v_cnidport ? volume->v_cnidport : Cnid_port);
-    
-    volume->v_cdb = cnid_open(volume->v_dbpath ? volume->v_dbpath : volume->v_path,
+
+#if 0
+/* Found this in branch dir-rewrite, maybe we want to use it sometimes */
+
+    /* Legacy pre 2.1 way of sharing eg CD-ROM */
+    if (strcmp(volume->v_cnidscheme, "last") == 0) {
+        /* "last" is gone. We support it by switching to in-memory "tdb" */
+        volume->v_cnidscheme = strdup("tdb");
+        flags |= CNID_FLAG_MEMORY;
+    }
+
+    /* New way of sharing CD-ROM */
+    if (volume->v_flags & AFPVOL_CDROM) {
+        flags |= CNID_FLAG_MEMORY;
+        if (strcmp(volume->v_cnidscheme, "tdb") != 0) {
+            free(volume->v_cnidscheme);
+            volume->v_cnidscheme = strdup("tdb");
+            LOG(log_info, logtype_afpd, "Volume %s is ejectable, switching to scheme %s.",
+                volume->v_path, volume->v_cnidscheme);
+        }
+    }
+#endif
+
+    volume->v_cdb = cnid_open(volume->v_path,
                               volume->v_umask,
                               volume->v_cnidscheme,
                               flags,
                               volume->v_cnidserver ? volume->v_cnidserver : Cnid_srv,
                               volume->v_cnidport ? volume->v_cnidport : Cnid_port);
 
-    if (!volume->v_cdb) {
+    if ( ! volume->v_cdb && ! (flags & CNID_FLAG_MEMORY)) {
+        /* The first attempt failed and it wasn't yet an attempt to open in-memory */
         LOG(log_error, logtype_afpd, "Can't open volume \"%s\" CNID backend \"%s\" ",
             volume->v_path, volume->v_cnidscheme);
-        flags |= CNID_FLAG_MEMORY;
         LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
             volume->v_path);
+        flags |= CNID_FLAG_MEMORY;
         volume->v_cdb = cnid_open (volume->v_path, volume->v_umask, "tdb", flags, NULL, NULL);
 #ifdef SERVERTEXT
         /* kill ourself with SIGUSR2 aka msg pending */
@@ -1870,11 +2018,11 @@ static int volume_openDB(struct vol *volume)
     return (!volume->v_cdb)?-1:0;
 }
 
-/* 
-   Check if the underlying filesystem supports EAs for ea:sys volumes.
-   If not, switch to ea:ad.
-   As we can't check (requires write access) on ro-volumes, we switch ea:auto
-   volumes that are options:ro to ea:none.
+/*
+  Check if the underlying filesystem supports EAs for ea:sys volumes.
+  If not, switch to ea:ad.
+  As we can't check (requires write access) on ro-volumes, we switch ea:auto
+  volumes that are options:ro to ea:none.
 */
 static void check_ea_sys_support(struct vol *vol)
 {
@@ -1885,7 +2033,7 @@ static void check_ea_sys_support(struct vol *vol)
     if (vol->v_vfs_ea == AFPVOL_EA_AUTO) {
 
         if ((vol->v_flags & AFPVOL_RO) == AFPVOL_RO) {
-            LOG(log_info, logtype_logger, "read-only volume '%s', can't test for EA support, disabling EAs", vol->v_localname);
+            LOG(log_info, logtype_afpd, "read-only volume '%s', can't test for EA support, disabling EAs", vol->v_localname);
             vol->v_vfs_ea = AFPVOL_EA_NONE;
             return;
         }
@@ -1895,7 +2043,7 @@ static void check_ea_sys_support(struct vol *vol)
         process_uid = geteuid();
         if (process_uid)
             if (seteuid(0) == -1) {
-                LOG(log_error, logtype_logger, "check_ea_sys_support: can't seteuid(0): %s", strerror(errno));
+                LOG(log_error, logtype_afpd, "check_ea_sys_support: can't seteuid(0): %s", strerror(errno));
                 exit(EXITERR_SYS);
             }
 
@@ -1910,7 +2058,7 @@ static void check_ea_sys_support(struct vol *vol)
 
         if (process_uid) {
             if (seteuid(process_uid) == -1) {
-                LOG(log_error, logtype_logger, "can't seteuid back %s", strerror(errno));
+                LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
                 exit(EXITERR_SYS);
             }
         }
@@ -2039,7 +2187,7 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
     if ((tmp = strdup(volume->v_path)) == NULL) {
         free(volume->v_path);
         return AFPERR_MISC;
-    } 
+    }
     free(volume->v_path);
     volume->v_path = tmp;
 #endif
@@ -2053,15 +2201,12 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
      * FIXME file size
      */
     if (utf8_encoding()) {
-        volume->max_filename = 255;
+        volume->max_filename = UTF8FILELEN_EARLY;
     }
     else {
         volume->max_filename = MACFILELEN;
     }
 
-    volume->v_dir = volume->v_root = NULL;
-    volume->v_hash = NULL;
-
     volume->v_flags |= AFPVOL_OPEN;
     volume->v_cdb = NULL;
 
@@ -2080,22 +2225,23 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
     else if (*(vol_uname + 1) != '\0')
         vol_uname++;
 
-    if ((dir = dirnew(vol_mname, vol_uname) ) == NULL) {
+    if ((dir = dir_new(vol_mname,
+                       vol_uname,
+                       volume,
+                       DIRDID_ROOT_PARENT,
+                       DIRDID_ROOT,
+                       bfromcstr(volume->v_path),
+                       st.st_ctime)
+            ) == NULL) {
         free(vol_mname);
         LOG(log_error, logtype_afpd, "afp_openvol(%s): malloc: %s", volume->v_path, strerror(errno) );
         ret = AFPERR_MISC;
         goto openvol_err;
     }
     free(vol_mname);
+    volume->v_root = dir;
+    curdir = dir;
 
-    dir->d_did = DIRDID_ROOT;
-    dir->d_color = DIRTREE_COLOR_BLACK; /* root node is black */
-    dir->d_m_name_ucs2 = strdup_w(volume->v_name);
-    volume->v_dir = volume->v_root = dir;
-    volume->v_curdir = NULL;
-    volume->v_hash = dirhash();
-
-    curdir = volume->v_dir;
     if (volume_openDB(volume) < 0) {
         LOG(log_error, logtype_afpd, "Fatal error: cannot open CNID or invalid CNID backend for %s: %s",
             volume->v_path, volume->v_cnidscheme);
@@ -2134,16 +2280,15 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t
         }
         else {
             p = Trash;
-            cname( volume, volume->v_dir, &p );
+            cname( volume, volume->v_root, &p );
         }
         return( AFP_OK );
     }
 
 openvol_err:
-    if (volume->v_dir) {
-        hash_free( volume->v_hash);
-        dirfree( volume->v_dir );
-        volume->v_dir = volume->v_root = NULL;
+    if (volume->v_root) {
+        dir_free( volume->v_root );
+        volume->v_root = NULL;
     }
 
     volume->v_flags &= ~AFPVOL_OPEN;
@@ -2161,9 +2306,8 @@ static void closevol(struct vol *vol)
     if (!vol)
         return;
 
-    hash_free( vol->v_hash);
-    dirfree( vol->v_root );
-    vol->v_dir = NULL;
+    dir_free( vol->v_root );
+    vol->v_root = NULL;
     if (vol->v_cdb != NULL) {
         cnid_close(vol->v_cdb);
         vol->v_cdb = NULL;
@@ -2204,7 +2348,7 @@ static void deletevol(struct vol *vol)
     if ( ovol != NULL ) {
         /* Even if chdir fails, we can't say afp_closevol fails. */
         if ( chdir( ovol->v_path ) == 0 ) {
-            curdir = ovol->v_dir;
+            curdir = ovol->v_root;
         }
     }
 
@@ -2231,6 +2375,7 @@ int afp_closevol(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_
     }
 
     deletevol(vol);
+    current_vol = NULL;
 
     return( AFP_OK );
 }
@@ -2253,6 +2398,8 @@ struct vol *getvolbyvid(const u_int16_t vid )
     set_uidgid ( vol );
 #endif /* FORCE_UIDGID */
 
+    current_vol = vol;
+
     return( vol );
 }
 
@@ -2510,7 +2657,7 @@ static int create_special_folder (const struct vol *vol, const struct _special_f
             free(q);
             return (-1);
         }
-        
+
         ad_setname(&ad, folder->name);
 
         ad_getattr(&ad, &attr);
@@ -2544,3 +2691,111 @@ static void handle_special_folders (const struct vol * vol)
     }
 }
 
+const struct vol *getvolumes(void)
+{
+    return Volumes;
+}
+
+void unload_volumes_and_extmap(void)
+{
+    LOG(log_debug, logtype_afpd, "unload_volumes_and_extmap");
+    free_extmap();
+    free_volumes();
+}
+
+/* 
+ * Get a volumes UUID from the config file.
+ * If there is none, it is generated and stored there.
+ *
+ * Returns pointer to allocated storage on success, NULL on error.
+ */
+static char *get_vol_uuid(const AFPObj *obj, const char *volname)
+{
+    char *volname_conf;
+    char buf[1024], uuid[UUID_PRINTABLE_STRING_LENGTH], *p;
+    FILE *fp;
+    struct stat tmpstat;
+    int fd;
+    
+    if ((fp = fopen(obj->options.uuidconf, "r")) != NULL) {  /* read open? */
+        /* scan in the conf file */
+        while (fgets(buf, sizeof(buf), fp) != NULL) { 
+            p = buf;
+            while (p && isblank(*p))
+                p++;
+            if (!p || (*p == '#') || (*p == '\n'))
+                continue;                             /* invalid line */
+            if (*p == '"') {
+                p++;
+                if ((volname_conf = strtok( p, "\"" )) == NULL)
+                    continue;                         /* syntax error */
+            } else {
+                if ((volname_conf = strtok( p, " \t" )) == NULL)
+                    continue;                         /* syntax error: invalid name */
+            }
+            p = strchr(p, '\0');
+            p++;
+            if (*p == '\0')
+                continue;                             /* syntax error */
+            
+            if (strcmp(volname, volname_conf) != 0)
+                continue;                             /* another volume name */
+                
+            while (p && isblank(*p))
+                p++;
+
+            if (sscanf(p, "%36s", uuid) == 1 ) {
+                for (int i=0; uuid[i]; i++)
+                    uuid[i] = toupper(uuid[i]);
+                LOG(log_debug, logtype_afpd, "get_uuid('%s'): UUID: '%s'", volname, uuid);
+                fclose(fp);
+                return strdup(uuid);
+            }
+        }
+    }
+
+    if (fp)
+        fclose(fp);
+
+    /*  not found or no file, reopen in append mode */
+
+    if (stat(obj->options.uuidconf, &tmpstat)) {                /* no file */
+        if (( fd = creat(obj->options.uuidconf, 0644 )) < 0 ) {
+            LOG(log_error, logtype_afpd, "ERROR: Cannot create %s (%s).",
+                obj->options.uuidconf, strerror(errno));
+            return NULL;
+        }
+        if (( fp = fdopen( fd, "w" )) == NULL ) {
+            LOG(log_error, logtype_afpd, "ERROR: Cannot fdopen %s (%s).",
+                obj->options.uuidconf, strerror(errno));
+            close(fd);
+            return NULL;
+        }
+    } else if ((fp = fopen(obj->options.uuidconf, "a+")) == NULL) { /* not found */
+        LOG(log_error, logtype_afpd, "Cannot create or append to %s (%s).",
+            obj->options.uuidconf, strerror(errno));
+        return NULL;
+    }
+    fseek(fp, 0L, SEEK_END);
+    if(ftell(fp) == 0) {                     /* size = 0 */
+        fprintf(fp, "# DON'T TOUCH NOR COPY THOUGHTLESSLY!\n");
+        fprintf(fp, "# This file is auto-generated by afpd\n");
+        fprintf(fp, "# and stores UUIDs for TM volumes.\n\n");
+    } else {
+        fseek(fp, -1L, SEEK_END);
+        if(fgetc(fp) != '\n') fputc('\n', fp); /* last char is \n? */
+    }                    
+    
+    /* generate uuid and write to file */
+    atalk_uuid_t id;
+    const char *cp;
+    randombytes((void *)id, 16);
+    cp = uuid_bin2string(id);
+
+    LOG(log_debug, logtype_afpd, "get_uuid('%s'): generated UUID '%s'", volname, cp);
+
+    fprintf(fp, "\"%s\"\t%36s\n", volname, cp);
+    fclose(fp);
+    
+    return strdup(cp);
+}
index 63d090036bf713f955df7eecac383c772d7cbc4d..fd2d22514ff086d42daaf6eae389a0b3cf970e95 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: volume.h,v 1.36 2009-10-15 10:43:13 didg Exp $
- *
  * Copyright (c) 1990,1994 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
  */
 #include "hash.h"
 #endif
 
-extern struct vol      *getvolbyvid (const u_int16_t);
+extern struct vol       *getvolbyvid (const u_int16_t);
 extern int              ustatfs_getvolspace (const struct vol *,
             VolSpace *, VolSpace *,
             u_int32_t *);
 extern void             setvoltime (AFPObj *, struct vol *);
 extern int              pollvoltime (AFPObj *);
 extern void             load_volumes (AFPObj *obj);
+extern const struct vol *getvolumes(void);
+extern void             unload_volumes_and_extmap(void);
 
 /* FP functions */
 int afp_openvol      (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf,  size_t *rbuflen);
@@ -39,4 +39,6 @@ int afp_closevol     (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf,  size
 /* netatalk functions */
 extern void     close_all_vol   (void);
 
+struct vol *current_vol;        /* last volume from getvolbyvid() */
+
 #endif
index 1e4ea4fbfcc652eaf7e019b094d72f47a9adef68..2d2e5b0654780c671d654a208209c69c4ae06584 100644 (file)
 #include "nbp.h"
 #include "multicast.h"
 
-extern int     transition;
+extern int  transition;
 
-struct nbptab  *nbptab = NULL;
+struct nbptab   *nbptab = NULL;
 
-static 
+static
 void nbp_ack( int fd, int nh_op, int nh_id, struct sockaddr_at *to)
 {
-    struct nbphdr      nh;
-    char               *data, packet[ SZ_NBPHDR + 1 ];
+    struct nbphdr   nh;
+    char        *data, packet[ SZ_NBPHDR + 1 ];
 
     nh.nh_op = nh_op;
     nh.nh_cnt = 0;
@@ -59,47 +59,47 @@ void nbp_ack( int fd, int nh_op, int nh_id, struct sockaddr_at *to)
     memcpy( data, &nh, SZ_NBPHDR );
     data += SZ_NBPHDR;
     if ( sendto( fd, packet, data - packet, 0, (struct sockaddr *)to,
-           sizeof( struct sockaddr_at )) < 0 ) {
-       LOG(log_error, logtype_atalkd, "sendto: %s", strerror(errno) );
+                 sizeof( struct sockaddr_at )) < 0 ) {
+        LOG(log_error, logtype_atalkd, "sendto: %s", strerror(errno) );
     }
 }
 
 int nbp_packet(struct atport *ap, struct sockaddr_at *from, char *data, int len)
 {
-    struct nbphdr      nh;
-    struct nbptuple    nt;
-    struct nbpnve      nn;
-    struct sockaddr_at sat;
-    struct nbptab      *ntab;
-    struct ziptab      *zt=NULL;
-    struct interface   *iface;
-    struct list                *l;
-    struct rtmptab     *rtmp;
-    char               *end, *nbpop, *zonep, packet[ ATP_BUFSIZ ];
-    int                        n, i, cc, locallkup;
-    u_char             tmplen;
+    struct nbphdr   nh;
+    struct nbptuple nt;
+    struct nbpnve   nn;
+    struct sockaddr_at  sat;
+    struct nbptab   *ntab;
+    struct ziptab   *zt=NULL;
+    struct interface    *iface;
+    struct list     *l;
+    struct rtmptab  *rtmp;
+    char        *end, *nbpop, *zonep, packet[ ATP_BUFSIZ ];
+    int         n, i, cc, locallkup;
+    u_char      tmplen;
 
     end = data + len;
     if ( data >= end ) {
-       LOG(log_info, logtype_atalkd, "nbp_packet malformed packet" );
-       return 1;
+        LOG(log_info, logtype_atalkd, "nbp_packet malformed packet" );
+        return 1;
     }
     if ( *data++ != DDPTYPE_NBP ) {
-       LOG(log_info, logtype_atalkd, "nbp_packet bad ddp type" );
-       return 1;
+        LOG(log_info, logtype_atalkd, "nbp_packet bad ddp type" );
+        return 1;
     }
 
     if ( data + SZ_NBPHDR + SZ_NBPTUPLE > end ) {
-       LOG(log_info, logtype_atalkd, "nbp_packet: malformed packet" );
-       return 1;
+        LOG(log_info, logtype_atalkd, "nbp_packet: malformed packet" );
+        return 1;
     }
     memcpy( &nh, data, SZ_NBPHDR );
-    nbpop = data;                      /* remember for fwd and brrq */
+    nbpop = data;           /* remember for fwd and brrq */
     data += SZ_NBPHDR;
     if ( nh.nh_cnt != 1 ) {
-       LOG(log_info, logtype_atalkd, "nbp_packet: bad tuple count (%d/%d)", nh.nh_cnt,
-               nh.nh_op );
-       return 1;
+        LOG(log_info, logtype_atalkd, "nbp_packet: bad tuple count (%d/%d)", nh.nh_cnt,
+            nh.nh_op );
+        return 1;
     }
 
     memcpy( &nt, data, SZ_NBPTUPLE );
@@ -117,8 +117,8 @@ int nbp_packet(struct atport *ap, struct sockaddr_at *from, char *data, int len)
     /* object */
     tmplen = (u_char) *data;
     if ( data >= end || tmplen > 32 || data + tmplen > end ) {
-       LOG(log_info, logtype_atalkd, "nbp_packet: malformed packet" );
-       return 1;
+        LOG(log_info, logtype_atalkd, "nbp_packet: malformed packet" );
+        return 1;
     }
     nn.nn_objlen = tmplen;
     data++;
@@ -128,8 +128,8 @@ int nbp_packet(struct atport *ap, struct sockaddr_at *from, char *data, int len)
     /* type */
     tmplen = (u_char) *data;
     if ( data >= end || tmplen > 32 || data + tmplen > end ) {
-       LOG(log_info, logtype_atalkd, "nbp_packet: malformed packet" );
-       return 1;
+        LOG(log_info, logtype_atalkd, "nbp_packet: malformed packet" );
+        return 1;
     }
     nn.nn_typelen = tmplen;
     data++;
@@ -139,509 +139,511 @@ int nbp_packet(struct atport *ap, struct sockaddr_at *from, char *data, int len)
     /* zone */
     tmplen = (u_char) *data;
     if ( data >= end || tmplen > 32 || data + tmplen > end ) {
-       LOG(log_info, logtype_atalkd, "nbp_packet: malformed packet" );
-       return 1;
+        LOG(log_info, logtype_atalkd, "nbp_packet: malformed packet" );
+        return 1;
     }
-    zonep = data;                      /* remember for fwd */
+    zonep = data;           /* remember for fwd */
     nn.nn_zonelen = tmplen;
     data++;
     memcpy( nn.nn_zone, data, nn.nn_zonelen );
     data += nn.nn_zonelen;
 
     if ( data != end ) {
-       LOG(log_info, logtype_atalkd, "nbp_packet: malformed packet" );
-       return 1;
+        LOG(log_info, logtype_atalkd, "nbp_packet: malformed packet" );
+        return 1;
     }
 
     locallkup = 0;
     switch ( nh.nh_op ) {
 
     case NBPOP_RGSTR :
-       /*
-        * Find the ziptab entry for the zone we're trying to register in.
-        */
-       if ( nn.nn_zonelen == 0 ||
-               ( nn.nn_zonelen == 1 && *nn.nn_zone == '*' )) {
-           if ( interfaces->i_next->i_rt->rt_zt ) {
-               zt = (struct ziptab *)interfaces->i_next->i_rt->rt_zt->l_data;
-           } else {
-               zt = NULL;
-           }
-       } else {
-           for ( zt = ziptab; zt; zt = zt->zt_next ) {
-               if ( zt->zt_len == nn.nn_zonelen && strndiacasecmp( zt->zt_name,
-                       nn.nn_zone, zt->zt_len ) == 0 ) {
-                   break;
-               }
-           }
-           if ( zt == NULL ) {
-               nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
-               return 0;
-           }
-       }
-
-       /*
-        * Observe that we don't have to do any local-zone verification
-        * if the zone aleady has a multicast address set.
-        */
-       if ( zt != NULL && zt->zt_bcast == NULL ) {
-           /*
-            * Check if zone is associated with any of our local interfaces.
-            */
-           for ( iface = interfaces; iface; iface = iface->i_next ) {
-               for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
-                   if ( zt == (struct ziptab *)l->l_data ) {
-                       break;
-                   }
-               }
-               if ( l != NULL ) {
-                   break;
-               }
-           }
-           if ( iface == NULL ) {
-               nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
-               return 0;
-           }
-
-           /* calculate and save multicast address */
-           if (zone_bcast(zt) < 0) {
-               LOG(log_error, logtype_atalkd, "nbp_packet: zone_bcast");
-               return -1;
-           }
-
-           for ( iface = interfaces; iface; iface = iface->i_next ) {
-               if (( iface->i_flags & IFACE_PHASE2 ) == 0 ) {
-                   continue;
-               }
-               for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
-                   if ( zt == (struct ziptab *)l->l_data ) {
-                       /* add multicast */
-                       if (addmulti(iface->i_name, zt->zt_bcast) < 0) {
-                           LOG(log_error, logtype_atalkd, "nbp_packet: addmulti: %s",
-                                   strerror(errno) );
-                           return -1;
-                       }
-                   }
-               }
-           }
-       }
-
-       if (( ntab = (struct nbptab *)malloc( sizeof( struct nbptab )))
-               == NULL ) {
-           LOG(log_error, logtype_atalkd, "nbp_packet: malloc: %s", strerror(errno) );
-           return -1;
-       }
-       memcpy( &ntab->nt_nve, &nn, sizeof( struct nbpnve ));
-       ntab->nt_iface = ap->ap_iface;
-       ntab->nt_next = nbptab;
-       ntab->nt_prev = NULL;
-       if ( nbptab ) {
-           nbptab->nt_prev = ntab;
-       }
-       nbptab = ntab;
-
-       nbp_ack( ap->ap_fd, NBPOP_OK, (int)nh.nh_id, from );
-       break;
+        /*
+         * Find the ziptab entry for the zone we're trying to register in.
+         */
+        if ( nn.nn_zonelen == 0 ||
+             ( nn.nn_zonelen == 1 && *nn.nn_zone == '*' )) {
+            if ( interfaces->i_next->i_rt->rt_zt ) {
+                zt = (struct ziptab *)interfaces->i_next->i_rt->rt_zt->l_data;
+            } else {
+                zt = NULL;
+            }
+        } else {
+            for ( zt = ziptab; zt; zt = zt->zt_next ) {
+                if ( zt->zt_len == nn.nn_zonelen && strndiacasecmp( zt->zt_name,
+                                                                    nn.nn_zone, zt->zt_len ) == 0 ) {
+                    break;
+                }
+            }
+            if ( zt == NULL ) {
+                nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
+                return 0;
+            }
+        }
+
+        /*
+         * Observe that we don't have to do any local-zone verification
+         * if the zone aleady has a multicast address set.
+         */
+        if ( zt != NULL && zt->zt_bcast == NULL ) {
+            /*
+             * Check if zone is associated with any of our local interfaces.
+             */
+            for ( iface = interfaces; iface; iface = iface->i_next ) {
+                for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
+                    if ( zt == (struct ziptab *)l->l_data ) {
+                        break;
+                    }
+                }
+                if ( l != NULL ) {
+                    break;
+                }
+            }
+            if ( iface == NULL ) {
+                nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
+                return 0;
+            }
+
+            /* calculate and save multicast address */
+            if (zone_bcast(zt) < 0) {
+                LOG(log_error, logtype_atalkd, "nbp_packet: zone_bcast");
+                return -1;
+            }
+
+            for ( iface = interfaces; iface; iface = iface->i_next ) {
+                if (( iface->i_flags & IFACE_PHASE2 ) == 0 ) {
+                    continue;
+                }
+                for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
+                    if ( zt == (struct ziptab *)l->l_data ) {
+                        /* add multicast */
+                        if (addmulti(iface->i_name, zt->zt_bcast) < 0) {
+                            LOG(log_error, logtype_atalkd, "nbp_packet: addmulti: %s",
+                                strerror(errno) );
+                            return -1;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (( ntab = (struct nbptab *)malloc( sizeof( struct nbptab )))
+            == NULL ) {
+            LOG(log_error, logtype_atalkd, "nbp_packet: malloc: %s", strerror(errno) );
+            return -1;
+        }
+        memcpy( &ntab->nt_nve, &nn, sizeof( struct nbpnve ));
+        ntab->nt_iface = ap->ap_iface;
+        ntab->nt_next = nbptab;
+        ntab->nt_prev = NULL;
+        if ( nbptab ) {
+            nbptab->nt_prev = ntab;
+        }
+        nbptab = ntab;
+
+        nbp_ack( ap->ap_fd, NBPOP_OK, (int)nh.nh_id, from );
+        break;
 
     case NBPOP_UNRGSTR :
         /* deal with local zone info */
         if (( nn.nn_zonelen == 1 && *nn.nn_zone == '*' ) ||
-           ( nn.nn_zonelen == 0 )) {
-         locallkup = 1;
-         if ( interfaces->i_next->i_rt->rt_zt ) {
-           zt = (struct ziptab *)interfaces->i_next->i_rt->rt_zt->l_data;
-         } else {
-           zt = NULL;
-         }
-       }
-
-       /* remove from our data, perhaps removing a multicast address */
-       for ( ntab = nbptab; ntab; ntab = ntab->nt_next ) {
-           if ( ntab->nt_nve.nn_objlen != nn.nn_objlen ||
-                   strndiacasecmp( ntab->nt_nve.nn_obj, nn.nn_obj,
-                   nn.nn_objlen )) {
-               continue;
-           }
-           if ( ntab->nt_nve.nn_typelen != nn.nn_typelen ||
-                   strndiacasecmp( ntab->nt_nve.nn_type, nn.nn_type,
-                   nn.nn_typelen )) {
-               continue;
-           }
-           /*
-            * I *think* we really do check the zone, here.
-            *
-            * i changed it to better handle local zone cases as well.
-            * -- asun
-            */
-           
-           /* match local zones */
-           if (locallkup) {
-             /* ntab is also local zone */
-             if (( ntab->nt_nve.nn_zonelen == 1 &&
-                   *ntab->nt_nve.nn_zone == '*' ) || 
-                 (ntab->nt_nve.nn_zonelen == 0))
-               break;
-             
-             /* ntab is default zone */
-             if (zt && (zt->zt_len == ntab->nt_nve.nn_zonelen) &&
-                 (strndiacasecmp(ntab->nt_nve.nn_zone, zt->zt_name,
-                                 zt->zt_len) == 0)) {
-               break;
-             }
-           }
-           
-           /* match particular zone */
-           if ((ntab->nt_nve.nn_zonelen == nn.nn_zonelen) &&
-               (strndiacasecmp( ntab->nt_nve.nn_zone, nn.nn_zone,
-                               nn.nn_zonelen ) == 0)) {
-               break;
-           }
-       }
-       if ( ntab == NULL ) {
-           nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
-           return 0;
-       }
-
-       if ( ntab->nt_next != NULL ) {
-           ntab->nt_next->nt_prev = ntab->nt_prev;
-       }
-       if ( ntab->nt_prev != NULL ) {
-           ntab->nt_prev->nt_next = ntab->nt_next;
-       }
-       if ( ntab == nbptab ) {
-           nbptab = ntab->nt_next;
-       }
-
-       /*
-        * Check for another nbptab entry with the same zone.  If
-        * there isn't one, find the ziptab entry for the zone and
-        * remove the multicast address from the appropriate interfaces.
-        * XXX
-        */
-
-       nbp_ack( ap->ap_fd, NBPOP_OK, (int)nh.nh_id, from );
-       break;
+            ( nn.nn_zonelen == 0 )) {
+            locallkup = 1;
+            if ( interfaces->i_next->i_rt->rt_zt ) {
+                zt = (struct ziptab *)interfaces->i_next->i_rt->rt_zt->l_data;
+            } else {
+                zt = NULL;
+            }
+        }
+
+        /* remove from our data, perhaps removing a multicast address */
+        for ( ntab = nbptab; ntab; ntab = ntab->nt_next ) {
+            if ( ntab->nt_nve.nn_objlen != nn.nn_objlen ||
+                 strndiacasecmp( ntab->nt_nve.nn_obj, nn.nn_obj,
+                                 nn.nn_objlen )) {
+                continue;
+            }
+            if ( ntab->nt_nve.nn_typelen != nn.nn_typelen ||
+                 strndiacasecmp( ntab->nt_nve.nn_type, nn.nn_type,
+                                 nn.nn_typelen )) {
+                continue;
+            }
+            /*
+             * I *think* we really do check the zone, here.
+             *
+             * i changed it to better handle local zone cases as well.
+             * -- asun
+             */
+
+            /* match local zones */
+            if (locallkup) {
+                /* ntab is also local zone */
+                if (( ntab->nt_nve.nn_zonelen == 1 &&
+                      *ntab->nt_nve.nn_zone == '*' ) ||
+                    (ntab->nt_nve.nn_zonelen == 0))
+                    break;
+
+                /* ntab is default zone */
+                if (zt && (zt->zt_len == ntab->nt_nve.nn_zonelen) &&
+                    (strndiacasecmp(ntab->nt_nve.nn_zone, zt->zt_name,
+                                    zt->zt_len) == 0)) {
+                    break;
+                }
+            }
+
+            /* match particular zone */
+            if ((ntab->nt_nve.nn_zonelen == nn.nn_zonelen) &&
+                (strndiacasecmp( ntab->nt_nve.nn_zone, nn.nn_zone,
+                                 nn.nn_zonelen ) == 0)) {
+                break;
+            }
+        }
+        if ( ntab == NULL ) {
+            nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
+            return 0;
+        }
+
+        if ( ntab->nt_next != NULL ) {
+            ntab->nt_next->nt_prev = ntab->nt_prev;
+        }
+        if ( ntab->nt_prev != NULL ) {
+            ntab->nt_prev->nt_next = ntab->nt_next;
+        }
+        if ( ntab == nbptab ) {
+            nbptab = ntab->nt_next;
+        }
+
+        /*
+         * Check for another nbptab entry with the same zone.  If
+         * there isn't one, find the ziptab entry for the zone and
+         * remove the multicast address from the appropriate interfaces.
+         * XXX
+         */
+
+        nbp_ack( ap->ap_fd, NBPOP_OK, (int)nh.nh_id, from );
+        break;
 
     case NBPOP_BRRQ :
-       /*
-        * Couple of things:  1. Unless we have the -t flag (which is sort
-        * of a misnomer, since you need it if you're doing any phase 1
-        * work), always send NBPOP_FWD.  2. If we get a zone of '*',
-        * and we know what the sender meant by '*', we copy the real
-        * zone into the packet.
-        */
-       if ( nn.nn_zonelen == 0 ||
-               ( nn.nn_zonelen == 1 && *nn.nn_zone == '*' )) {
-           iface = ap->ap_iface;
-           if ( iface && iface->i_rt->rt_zt ) {
-               zt = (struct ziptab *)iface->i_rt->rt_zt->l_data;
-           } else if ( interfaces->i_next->i_rt->rt_zt ) {
-               zt = (struct ziptab *)interfaces->i_next->i_rt->rt_zt->l_data;
-           } else {
-               zt = NULL;
-           }
-
-           /*
-            * Copy zone into packet.  Note that we're changing len, data, and
-            * nbpop.  Later, we'll use ( data - len ) to mean the beginning
-            * of this packet.
-            */
-           if ( zt ) {
-               memcpy( packet, data - len, len );
-               nbpop = packet + ( len - ( data - nbpop ));
-               data = packet + ( len - ( data - zonep ));
-               *data++ = zt->zt_len;
-               memcpy( data, zt->zt_name, zt->zt_len );
-               data += zt->zt_len;
-               len = data - packet;
-           }
-       } else {
-           for ( zt = ziptab; zt; zt = zt->zt_next ) {
-               if ( zt->zt_len == nn.nn_zonelen && strndiacasecmp( zt->zt_name,
-                       nn.nn_zone, zt->zt_len ) == 0 ) {
-                   break;
-               }
-           }
-           if ( zt == NULL ) {
-               nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
-               return 0;
-           }
-       }
-
-       /*
-        * If we've got no zones, send out LKUP on the local net.
-        * Otherwise, look through the zone table.
-        */
-       if ( zt == NULL ) {
+        /*
+         * Couple of things:  1. Unless we have the -t flag (which is sort
+         * of a misnomer, since you need it if you're doing any phase 1
+         * work), always send NBPOP_FWD.  2. If we get a zone of '*',
+         * and we know what the sender meant by '*', we copy the real
+         * zone into the packet.
+         */
+        if ( nn.nn_zonelen == 0 ||
+             ( nn.nn_zonelen == 1 && *nn.nn_zone == '*' )) {
+            iface = ap->ap_iface;
+            if ( iface && iface->i_rt->rt_zt ) {
+                zt = (struct ziptab *)iface->i_rt->rt_zt->l_data;
+            } else if ( interfaces->i_next->i_rt->rt_zt ) {
+                zt = (struct ziptab *)interfaces->i_next->i_rt->rt_zt->l_data;
+            } else {
+                zt = NULL;
+            }
+
+            /*
+             * Copy zone into packet.  Note that we're changing len, data, and
+             * nbpop.  Later, we'll use ( data - len ) to mean the beginning
+             * of this packet.
+             */
+            if ( zt ) {
+                memcpy( packet, data - len, len );
+                nbpop = packet + ( len - ( data - nbpop ));
+                data = packet + ( len - ( data - zonep ));
+                *data++ = zt->zt_len;
+                memcpy( data, zt->zt_name, zt->zt_len );
+                data += zt->zt_len;
+                len = data - packet;
+            }
+        } else {
+            for ( zt = ziptab; zt; zt = zt->zt_next ) {
+                if ( zt->zt_len == nn.nn_zonelen && strndiacasecmp( zt->zt_name,
+                                                                    nn.nn_zone, zt->zt_len ) == 0 ) {
+                    break;
+                }
+            }
+            if ( zt == NULL ) {
+                nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
+                return 0;
+            }
+        }
+
+        /*
+         * If we've got no zones, send out LKUP on the local net.
+         * Otherwise, look through the zone table.
+         */
+        if ( zt == NULL ) {
 #ifdef BSD4_4
-           sat.sat_len = sizeof( struct sockaddr_at );
+            sat.sat_len = sizeof( struct sockaddr_at );
 #endif /* BSD4_4 */
-           sat.sat_family = AF_APPLETALK;
-           sat.sat_port = ap->ap_port;
-
-           nh.nh_op = NBPOP_LKUP;
-           memcpy( nbpop, &nh, SZ_NBPHDR );
-           sat.sat_addr.s_net = 0;                     /* XXX */
-           sat.sat_addr.s_node = ATADDR_BCAST;
-
-           /* Find the first non-loopback ap */
-           for ( iface = interfaces; iface; iface = iface->i_next ) {
-               if ((( iface->i_flags & IFACE_LOOPBACK ) == 0) &&
-                   (iface == ap->ap_iface || 
-                   (iface->i_flags & IFACE_ISROUTER))) {
-                   break;
-               }
-           }
-           if ( iface == NULL ) {
-               return 0;
-           }
-           for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
-               if ( ap->ap_packet == nbp_packet ) {
-                   break;
-               }
-           }
-
-           if ( sendto( ap->ap_fd, data - len, len, 0, (struct sockaddr *)&sat,
-                   sizeof( struct sockaddr_at )) < 0 ) {
-               LOG(log_error, logtype_atalkd, "nbp brrq sendto: %s", strerror(errno) );
-           }
-
-           locallkup = 1;
-       } else {
+            sat.sat_family = AF_APPLETALK;
+            sat.sat_port = ap->ap_port;
+
+            nh.nh_op = NBPOP_LKUP;
+            memcpy( nbpop, &nh, SZ_NBPHDR );
+            sat.sat_addr.s_net = 0;         /* XXX */
+            sat.sat_addr.s_node = ATADDR_BCAST;
+
+            /* Find the first non-loopback ap */
+            for ( iface = interfaces; iface; iface = iface->i_next ) {
+                if ((( iface->i_flags & IFACE_LOOPBACK ) == 0) &&
+                    (iface == ap->ap_iface ||
+                     (iface->i_flags & IFACE_ISROUTER))) {
+                    break;
+                }
+            }
+            if ( iface == NULL ) {
+                return 0;
+            }
+            for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
+                if ( ap->ap_packet == nbp_packet ) {
+                    break;
+                }
+            }
+            if (ap == NULL)
+                return 0;
+
+            if ( sendto( ap->ap_fd, data - len, len, 0, (struct sockaddr *)&sat,
+                         sizeof( struct sockaddr_at )) < 0 ) {
+                LOG(log_error, logtype_atalkd, "nbp brrq sendto: %s", strerror(errno) );
+            }
+
+            locallkup = 1;
+        } else {
 #ifdef BSD4_4
-           sat.sat_len = sizeof( struct sockaddr_at );
+            sat.sat_len = sizeof( struct sockaddr_at );
 #endif /* BSD4_4 */
-           sat.sat_family = AF_APPLETALK;
-           sat.sat_port = ap->ap_port;
-           for ( l = zt->zt_rt; l; l = l->l_next ) {
-               rtmp = (struct rtmptab *)l->l_data;
-
-               if ( rtmp->rt_gate == NULL ) {
-                   for ( iface = interfaces; iface;
-                           iface = iface->i_next ) {
-                       if ( iface->i_rt == rtmp ) {
-                           break;
-                       }
-                   }
-                   if ( !iface ) {
-                       LOG(log_error, logtype_atalkd, "nbp_packet: \
+            sat.sat_family = AF_APPLETALK;
+            sat.sat_port = ap->ap_port;
+            for ( l = zt->zt_rt; l; l = l->l_next ) {
+                rtmp = (struct rtmptab *)l->l_data;
+
+                if ( rtmp->rt_gate == NULL ) {
+                    for ( iface = interfaces; iface;
+                          iface = iface->i_next ) {
+                        if ( iface->i_rt == rtmp ) {
+                            break;
+                        }
+                    }
+                    if ( !iface ) {
+                        LOG(log_error, logtype_atalkd, "nbp_packet: \
 Can't find route's interface!" );
-                       return -1;
-                   }
-                   ap = iface->i_ports;
-               } else {
-                   ap = rtmp->rt_gate->g_iface->i_ports;
-               }
-               for ( ; ap; ap = ap->ap_next ) {
-                   if ( ap->ap_packet == nbp_packet ) {
-                       break;
-                   }
-               }
-               if ( !ap ) {
-                   LOG(log_error, logtype_atalkd, "nbp_packet: Can't find port!" );
-                   return -1;
-               }
-
-               if ( transition &&
-                       ( rtmp->rt_flags & RTMPTAB_EXTENDED ) == 0 ) {
-                   if ( rtmp->rt_gate == NULL ) {
-                       locallkup = 1;
-                   }
-                   nh.nh_op = NBPOP_LKUP;
-                   memcpy( nbpop, &nh, SZ_NBPHDR );
-                   sat.sat_addr.s_net = rtmp->rt_firstnet;
-                   sat.sat_addr.s_node = ATADDR_BCAST;
-               } else {
-                   if ( rtmp->rt_gate == NULL ) {
-                       nh.nh_op = NBPOP_LKUP;
-                       memcpy( nbpop, &nh, SZ_NBPHDR );
-                       sat.sat_addr.s_net = 0;
-                       sat.sat_addr.s_node = ATADDR_BCAST;
-                       locallkup = 1;
-                   } else {
-                       nh.nh_op = NBPOP_FWD;
-                       memcpy( nbpop, &nh, SZ_NBPHDR );
-                       sat.sat_addr.s_net = rtmp->rt_firstnet;
-                       sat.sat_addr.s_node = 0;
-                   }
-               }
-
-               if ( sendto( ap->ap_fd, data - len, len, 0,
-                       (struct sockaddr *)&sat,
-                       sizeof( struct sockaddr_at )) < 0 ) {
-                   LOG(log_error, logtype_atalkd, "nbp brrq sendto %u.%u: %s",
-                           ntohs( sat.sat_addr.s_net ), sat.sat_addr.s_node,
-                           strerror(errno) );
-                   continue;
-               }
-           }
-       }
-
-       if ( !locallkup ) {
-           break;
-       }
-       /*FALL THROUGH*/
+                        return -1;
+                    }
+                    ap = iface->i_ports;
+                } else {
+                    ap = rtmp->rt_gate->g_iface->i_ports;
+                }
+                for ( ; ap; ap = ap->ap_next ) {
+                    if ( ap->ap_packet == nbp_packet ) {
+                        break;
+                    }
+                }
+                if ( !ap ) {
+                    LOG(log_error, logtype_atalkd, "nbp_packet: Can't find port!" );
+                    return -1;
+                }
+
+                if ( transition &&
+                     ( rtmp->rt_flags & RTMPTAB_EXTENDED ) == 0 ) {
+                    if ( rtmp->rt_gate == NULL ) {
+                        locallkup = 1;
+                    }
+                    nh.nh_op = NBPOP_LKUP;
+                    memcpy( nbpop, &nh, SZ_NBPHDR );
+                    sat.sat_addr.s_net = rtmp->rt_firstnet;
+                    sat.sat_addr.s_node = ATADDR_BCAST;
+                } else {
+                    if ( rtmp->rt_gate == NULL ) {
+                        nh.nh_op = NBPOP_LKUP;
+                        memcpy( nbpop, &nh, SZ_NBPHDR );
+                        sat.sat_addr.s_net = 0;
+                        sat.sat_addr.s_node = ATADDR_BCAST;
+                        locallkup = 1;
+                    } else {
+                        nh.nh_op = NBPOP_FWD;
+                        memcpy( nbpop, &nh, SZ_NBPHDR );
+                        sat.sat_addr.s_net = rtmp->rt_firstnet;
+                        sat.sat_addr.s_node = 0;
+                    }
+                }
+
+                if ( sendto( ap->ap_fd, data - len, len, 0,
+                             (struct sockaddr *)&sat,
+                             sizeof( struct sockaddr_at )) < 0 ) {
+                    LOG(log_error, logtype_atalkd, "nbp brrq sendto %u.%u: %s",
+                        ntohs( sat.sat_addr.s_net ), sat.sat_addr.s_node,
+                        strerror(errno) );
+                    continue;
+                }
+            }
+        }
+
+        if ( !locallkup ) {
+            break;
+        }
+        /*FALL THROUGH*/
 
     case NBPOP_FWD :
         /* send lkup on net. we need to make sure we're a router. */
         if ( !locallkup && (ap->ap_iface->i_flags & IFACE_ISROUTER)) {
-           nh.nh_op = NBPOP_LKUP;
-           memcpy( nbpop, &nh, SZ_NBPHDR );
-           from->sat_addr.s_net = 0;
-           from->sat_addr.s_node = ATADDR_BCAST;
-           if ( sendto( ap->ap_fd, data - len, len, 0, (struct sockaddr *)from,
-                   sizeof( struct sockaddr_at )) < 0 ) {
-               LOG(log_error, logtype_atalkd, "nbp fwd sendto %u.%u: %s",
-                       ntohs( from->sat_addr.s_net ), from->sat_addr.s_node,
-                       strerror(errno) );
-               return 0;
-           }
-       }
-       /*FALL THROUGH*/
+            nh.nh_op = NBPOP_LKUP;
+            memcpy( nbpop, &nh, SZ_NBPHDR );
+            from->sat_addr.s_net = 0;
+            from->sat_addr.s_node = ATADDR_BCAST;
+            if ( sendto( ap->ap_fd, data - len, len, 0, (struct sockaddr *)from,
+                         sizeof( struct sockaddr_at )) < 0 ) {
+                LOG(log_error, logtype_atalkd, "nbp fwd sendto %u.%u: %s",
+                    ntohs( from->sat_addr.s_net ), from->sat_addr.s_node,
+                    strerror(errno) );
+                return 0;
+            }
+        }
+        /*FALL THROUGH*/
 
     case NBPOP_LKUP :
-       /* search our data */
-       n = i = 0;
-       data = packet + 1 + SZ_NBPHDR;
-       end = packet + sizeof( packet );
-
-       for ( ntab = nbptab; ntab; ntab = ntab->nt_next ) {
-           /* don't send out entries if we don't want to route. */
-           if ((ap->ap_iface != ntab->nt_iface) && 
-               (ntab->nt_iface->i_flags & IFACE_ISROUTER) == 0) {
-             continue;
-           }
-
-           if ( nn.nn_objlen != 1 || *nn.nn_obj != '=' ) {
-               if ( ntab->nt_nve.nn_objlen != nn.nn_objlen ||
-                       strndiacasecmp( ntab->nt_nve.nn_obj, nn.nn_obj,
-                       nn.nn_objlen )) {
-                   continue;
-               }
-           }
-
-           if ( nn.nn_typelen != 1 || *nn.nn_type != '=' ) {
-               if ( ntab->nt_nve.nn_typelen != nn.nn_typelen ||
-                       strndiacasecmp( ntab->nt_nve.nn_type, nn.nn_type,
-                       nn.nn_typelen )) {
-                   continue;
-               }
-           }
-
-           if ( nn.nn_zonelen != 0 &&
-                   ( nn.nn_zonelen != 1 || *nn.nn_zone != '*' )) {
-               if ( ntab->nt_nve.nn_zonelen == 0 ||
-                       ( ntab->nt_nve.nn_zonelen == 1 &&
-                       *ntab->nt_nve.nn_zone == '*' )) {
-                   if ( interfaces->i_next->i_rt->rt_zt ) {
-                       zt = (struct ziptab *)interfaces->i_next->i_rt->
-                               rt_zt->l_data;
-                       if ( zt->zt_len != nn.nn_zonelen ||
-                               strndiacasecmp( zt->zt_name, nn.nn_zone,
-                               zt->zt_len )) {
-                           continue;
-                       }
-                   }
-               } else {
-                   if ( ntab->nt_nve.nn_zonelen != nn.nn_zonelen ||
-                           strndiacasecmp( ntab->nt_nve.nn_zone, nn.nn_zone,
-                           nn.nn_zonelen )) {
-                       continue;
-                   }
-               }
-           }
-
-           /*
-            * Another tuple won't fit. Send what we've already
-            * got, and start the next packet.
-            */
-           if ( n > 14 || data + SZ_NBPTUPLE + 3 + ntab->nt_nve.nn_objlen +
-                   ntab->nt_nve.nn_typelen + ntab->nt_nve.nn_zonelen > end ) {
-               nh.nh_op = NBPOP_LKUPREPLY;
-               nh.nh_cnt = n;
-               cc = data - packet;
-               data = packet;
-               *data++ = DDPTYPE_NBP;
-               memcpy( data, &nh, SZ_NBPHDR );
-
-               if ( sendto( ap->ap_fd, packet, cc, 0,
-                       (struct sockaddr *)&nn.nn_sat,
-                       sizeof( struct sockaddr_at )) < 0 ) {
-                   LOG(log_error, logtype_atalkd, "nbp lkup sendto %u.%u: %s",
-                           ntohs( nn.nn_sat.sat_addr.s_net ),
-                           nn.nn_sat.sat_addr.s_node,
-                           strerror(errno) );
-                   return 0;
-               }
-
-               n = 0;
-               data = packet + 1 + SZ_NBPHDR;
-               end = packet + sizeof( packet );
-           }
-
-           nt.nt_net = ntab->nt_nve.nn_sat.sat_addr.s_net;
-           nt.nt_node = ntab->nt_nve.nn_sat.sat_addr.s_node;
-           nt.nt_port = ntab->nt_nve.nn_sat.sat_port;
-           /*
-            * Right now, we'll just give each name a unique enum.  In
-            * the future, we might need to actually assign and save
-            * an enum, based on the associated address.  For the moment,
-            * the enums will be unique and constant, since the order
-            * is fixed.
-            */
-           nt.nt_enum = i++;
-
-           memcpy( data, &nt, SZ_NBPTUPLE );
-           data += SZ_NBPTUPLE;
-
-           *data++ = ntab->nt_nve.nn_objlen;
-           memcpy( data, ntab->nt_nve.nn_obj, ntab->nt_nve.nn_objlen );
-           data += ntab->nt_nve.nn_objlen;
-
-           *data++ = ntab->nt_nve.nn_typelen;
-           memcpy(data, ntab->nt_nve.nn_type, ntab->nt_nve.nn_typelen );
-           data += ntab->nt_nve.nn_typelen;
-
-           /*
-            * Macs won't see something with a zone of 0 length.  We
-            * will always return '*' instead.  Perhaps we should
-            * unconditionally return the real zone?
-            */
-           if ( ntab->nt_nve.nn_zonelen ) {
-               *data++ = ntab->nt_nve.nn_zonelen;
-               memcpy( data, ntab->nt_nve.nn_zone, ntab->nt_nve.nn_zonelen );
-               data += ntab->nt_nve.nn_zonelen;
-           } else {
-               *data++ = 1;
-               *data++ = '*';
-           }
-
-           n++;
-       }
-
-       if ( n != 0 ) {
-           nh.nh_op = NBPOP_LKUPREPLY;
-           nh.nh_cnt = n;
-           cc = data - packet;
-           data = packet;
-           *data++ = DDPTYPE_NBP;
-           memcpy( data, &nh, SZ_NBPHDR );
-
-           if ( sendto( ap->ap_fd, packet, cc, 0,
-                   (struct sockaddr *)&nn.nn_sat,
-                   sizeof( struct sockaddr_at )) < 0 ) {
-               LOG(log_error, logtype_atalkd, "nbp lkup sendto %u.%u: %s",
-                       ntohs( nn.nn_sat.sat_addr.s_net ),
-                       nn.nn_sat.sat_addr.s_node,
-                       strerror(errno) );
-               return 0;
-           }
-       }
-       break;
+        /* search our data */
+        n = i = 0;
+        data = packet + 1 + SZ_NBPHDR;
+        end = packet + sizeof( packet );
+
+        for ( ntab = nbptab; ntab; ntab = ntab->nt_next ) {
+            /* don't send out entries if we don't want to route. */
+            if ((ap->ap_iface != ntab->nt_iface) &&
+                (ntab->nt_iface->i_flags & IFACE_ISROUTER) == 0) {
+                continue;
+            }
+
+            if ( nn.nn_objlen != 1 || *nn.nn_obj != '=' ) {
+                if ( ntab->nt_nve.nn_objlen != nn.nn_objlen ||
+                     strndiacasecmp( ntab->nt_nve.nn_obj, nn.nn_obj,
+                                     nn.nn_objlen )) {
+                    continue;
+                }
+            }
+
+            if ( nn.nn_typelen != 1 || *nn.nn_type != '=' ) {
+                if ( ntab->nt_nve.nn_typelen != nn.nn_typelen ||
+                     strndiacasecmp( ntab->nt_nve.nn_type, nn.nn_type,
+                                     nn.nn_typelen )) {
+                    continue;
+                }
+            }
+
+            if ( nn.nn_zonelen != 0 &&
+                 ( nn.nn_zonelen != 1 || *nn.nn_zone != '*' )) {
+                if ( ntab->nt_nve.nn_zonelen == 0 ||
+                     ( ntab->nt_nve.nn_zonelen == 1 &&
+                       *ntab->nt_nve.nn_zone == '*' )) {
+                    if ( interfaces->i_next->i_rt->rt_zt ) {
+                        zt = (struct ziptab *)interfaces->i_next->i_rt->
+                            rt_zt->l_data;
+                        if ( zt->zt_len != nn.nn_zonelen ||
+                             strndiacasecmp( zt->zt_name, nn.nn_zone,
+                                             zt->zt_len )) {
+                            continue;
+                        }
+                    }
+                } else {
+                    if ( ntab->nt_nve.nn_zonelen != nn.nn_zonelen ||
+                         strndiacasecmp( ntab->nt_nve.nn_zone, nn.nn_zone,
+                                         nn.nn_zonelen )) {
+                        continue;
+                    }
+                }
+            }
+
+            /*
+             * Another tuple won't fit. Send what we've already
+             * got, and start the next packet.
+             */
+            if ( n > 14 || data + SZ_NBPTUPLE + 3 + ntab->nt_nve.nn_objlen +
+                 ntab->nt_nve.nn_typelen + ntab->nt_nve.nn_zonelen > end ) {
+                nh.nh_op = NBPOP_LKUPREPLY;
+                nh.nh_cnt = n;
+                cc = data - packet;
+                data = packet;
+                *data++ = DDPTYPE_NBP;
+                memcpy( data, &nh, SZ_NBPHDR );
+
+                if ( sendto( ap->ap_fd, packet, cc, 0,
+                             (struct sockaddr *)&nn.nn_sat,
+                             sizeof( struct sockaddr_at )) < 0 ) {
+                    LOG(log_error, logtype_atalkd, "nbp lkup sendto %u.%u: %s",
+                        ntohs( nn.nn_sat.sat_addr.s_net ),
+                        nn.nn_sat.sat_addr.s_node,
+                        strerror(errno) );
+                    return 0;
+                }
+
+                n = 0;
+                data = packet + 1 + SZ_NBPHDR;
+                end = packet + sizeof( packet );
+            }
+
+            nt.nt_net = ntab->nt_nve.nn_sat.sat_addr.s_net;
+            nt.nt_node = ntab->nt_nve.nn_sat.sat_addr.s_node;
+            nt.nt_port = ntab->nt_nve.nn_sat.sat_port;
+            /*
+             * Right now, we'll just give each name a unique enum.  In
+             * the future, we might need to actually assign and save
+             * an enum, based on the associated address.  For the moment,
+             * the enums will be unique and constant, since the order
+             * is fixed.
+             */
+            nt.nt_enum = i++;
+
+            memcpy( data, &nt, SZ_NBPTUPLE );
+            data += SZ_NBPTUPLE;
+
+            *data++ = ntab->nt_nve.nn_objlen;
+            memcpy( data, ntab->nt_nve.nn_obj, ntab->nt_nve.nn_objlen );
+            data += ntab->nt_nve.nn_objlen;
+
+            *data++ = ntab->nt_nve.nn_typelen;
+            memcpy(data, ntab->nt_nve.nn_type, ntab->nt_nve.nn_typelen );
+            data += ntab->nt_nve.nn_typelen;
+
+            /*
+             * Macs won't see something with a zone of 0 length.  We
+             * will always return '*' instead.  Perhaps we should
+             * unconditionally return the real zone?
+             */
+            if ( ntab->nt_nve.nn_zonelen ) {
+                *data++ = ntab->nt_nve.nn_zonelen;
+                memcpy( data, ntab->nt_nve.nn_zone, ntab->nt_nve.nn_zonelen );
+                data += ntab->nt_nve.nn_zonelen;
+            } else {
+                *data++ = 1;
+                *data++ = '*';
+            }
+
+            n++;
+        }
+
+        if ( n != 0 ) {
+            nh.nh_op = NBPOP_LKUPREPLY;
+            nh.nh_cnt = n;
+            cc = data - packet;
+            data = packet;
+            *data++ = DDPTYPE_NBP;
+            memcpy( data, &nh, SZ_NBPHDR );
+
+            if ( sendto( ap->ap_fd, packet, cc, 0,
+                         (struct sockaddr *)&nn.nn_sat,
+                         sizeof( struct sockaddr_at )) < 0 ) {
+                LOG(log_error, logtype_atalkd, "nbp lkup sendto %u.%u: %s",
+                    ntohs( nn.nn_sat.sat_addr.s_net ),
+                    nn.nn_sat.sat_addr.s_node,
+                    strerror(errno) );
+                return 0;
+            }
+        }
+        break;
 
     default :
-       LOG(log_info, logtype_atalkd, "nbp_packet: bad op (%d)", nh.nh_op );
-       return 1;
+        LOG(log_info, logtype_atalkd, "nbp_packet: bad op (%d)", nh.nh_op );
+        return 1;
     }
 
     return 0;
index a8866351810149eb56e564705c596ea418b44529..802ad3abb64e90f620a319a678a22d25599a9978 100644 (file)
@@ -10,17 +10,23 @@ endif
 cnid_dbd_SOURCES = dbif.c pack.c comm.c db_param.c main.c \
                    dbd_add.c dbd_get.c dbd_resolve.c dbd_lookup.c \
                    dbd_update.c dbd_delete.c dbd_getstamp.c \
-                   dbd_rebuild_add.c dbd_dbcheck.c
-cnid_dbd_LDADD = $(top_builddir)/libatalk/libatalk.la @BDB_LIBS@
+                   dbd_rebuild_add.c dbd_dbcheck.c dbd_search.c
+cnid_dbd_LDADD = $(top_builddir)/libatalk/libatalk.la @BDB_LIBS@ @ACL_LIBS@
 
 cnid_metad_SOURCES = cnid_metad.c usockfd.c db_param.c
-cnid_metad_LDADD = $(top_builddir)/libatalk/libatalk.la
+cnid_metad_LDADD = $(top_builddir)/libatalk/libatalk.la @ACL_LIBS@
 
-dbd_SOURCES = cmd_dbd.c cmd_dbd_scanvol.c \
-                         dbif.c pack.c \
-                         dbd_delete.c dbd_update.c dbd_add.c dbd_lookup.c \
-                         dbd_rebuild_add.c dbd_getstamp.c
-dbd_LDADD = $(top_builddir)/libatalk/libatalk.la @BDB_LIBS@
+dbd_SOURCES = cmd_dbd.c \
+       cmd_dbd_scanvol.c \
+       dbif.c pack.c \
+       dbd_add.c \
+       dbd_delete.c \
+       dbd_getstamp.c \
+       dbd_lookup.c \
+       dbd_rebuild_add.c \
+       dbd_resolve.c \
+       dbd_update.c
+dbd_LDADD = $(top_builddir)/libatalk/libatalk.la @BDB_LIBS@ @ACL_LIBS@
 
 noinst_HEADERS = dbif.h pack.h db_param.h dbd.h usockfd.h comm.h cmd_dbd.h
 
index dc4c3923d90eeb1a0e47c3c24bb4b4df32d4333f..2805b6fcf5c91423f36dd7e7c43784943c74300f 100644 (file)
@@ -1,6 +1,4 @@
 /* 
-   $Id: cmd_dbd.c,v 1.26 2010-04-20 16:46:20 hat001 Exp $
-
    Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
    
    This program is free software; you can redistribute it and/or modify
@@ -84,6 +82,7 @@
 
 int nocniddb = 0;               /* Dont open CNID database, only scan filesystem */
 volatile sig_atomic_t alarmed;
+struct volinfo volinfo; /* needed by pack.c:idxname() */
 
 static DBD *dbd;
 static int verbose;             /* Logging flag */
@@ -92,14 +91,17 @@ static struct db_param db_param = {
     NULL,                       /* Volume dirpath */
     1,                          /* bdb logfile autoremove */
     64 * 1024,                  /* bdb cachesize (64 MB) */
-    -1,                         /* not used ... */
-    -1,
-    "",
-    -1,
-    -1,
-    -1
+    DEFAULT_MAXLOCKS,           /* maxlocks */
+    DEFAULT_MAXLOCKOBJS,        /* maxlockobjs */
+    0,                          /* flush_interval */
+    0,                          /* flush_frequency */
+    1000,                       /* txn_frequency */
+    0,                          /* usock_file */
+    -1,                         /* fd_table_size */
+    -1,                         /* idle_timeout */
+    -1                          /* max_vols */
 };
-static char dbpath[PATH_MAX];   /* Path to the dbd database */
+static char dbpath[MAXPATHLEN+1];   /* Path to the dbd database */
 
 /* 
    Provide some logging
@@ -229,7 +231,7 @@ static void free_lock(int lockfd)
 
 static void usage (void)
 {
-    printf("Usage: dbd [-e|-v|-x] -d [-i] | -s [-c|-n]| -r [-c|-f] | -u <path to netatalk volume>\n"
+    printf("Usage: dbd [-e|-t|-v|-x] -d [-i] | -s [-c|-n]| -r [-c|-f] | -u <path to netatalk volume>\n"
            "dbd can dump, scan, reindex and rebuild Netatalk dbd CNID databases.\n"
            "dbd must be run with appropiate permissions i.e. as root.\n\n"
            "Main commands are:\n"
@@ -266,6 +268,7 @@ static void usage (void)
            "General options:\n"
            "   -e only work on inactive volumes and lock them (exclusive)\n"
            "   -x rebuild indexes (just for completeness, mostly useless!)\n"
+           "   -t show statistics while running\n"
            "   -v verbose\n\n"
            "WARNING:\n"
            "For -r -f restore of the CNID database from the adouble files, the CNID must of course\n"
@@ -279,7 +282,6 @@ int main(int argc, char **argv)
     int dump=0, scan=0, rebuild=0, prep_upgrade=0, rebuildindexes=0, dumpindexes=0, force=0;
     dbd_flags_t flags = 0;
     char *volpath;
-    struct volinfo volinfo;
     int cdir;
 
     if (geteuid() != 0) {
@@ -289,7 +291,7 @@ int main(int argc, char **argv)
     /* Inhereting perms in ad_mkdir etc requires this */
     ad_setfuid(0);
 
-    while ((c = getopt(argc, argv, ":cdefinrsuvx")) != -1) {
+    while ((c = getopt(argc, argv, ":cdefinrstuvx")) != -1) {
         switch(c) {
         case 'c':
             flags |= DBD_FLAGS_CLEANUP;
@@ -310,6 +312,9 @@ int main(int argc, char **argv)
         case 'r':
             rebuild = 1;
             break;
+        case 't':
+            flags |= DBD_FLAGS_STATS;
+            break;
         case 'u':
             prep_upgrade = 1;
             break;
@@ -380,12 +385,25 @@ int main(int argc, char **argv)
         exit(EXIT_FAILURE);        
     }
 
+    /* Enuser dbpath is there, create if necessary */
+    struct stat st;
+    if (stat(volinfo.v_dbpath, &st) != 0) {
+        if (errno != ENOENT) {
+            dbd_log( LOGSTD, "Can't stat dbpath \"%s\": %s", volinfo.v_dbpath, strerror(errno));
+            exit(EXIT_FAILURE);        
+        }
+        if ((mkdir(volinfo.v_dbpath, 0755)) != 0) {
+            dbd_log( LOGSTD, "Can't create dbpath \"%s\": %s", dbpath, strerror(errno));
+            exit(EXIT_FAILURE);
+        }        
+    }
+
     /* Put "/.AppleDB" at end of volpath, get path from volinfo file */
-    if ( (strlen(volinfo.v_dbpath) + strlen("/.AppleDB")) > (PATH_MAX - 1) ) {
+    if ( (strlen(volinfo.v_dbpath) + strlen("/.AppleDB")) > MAXPATHLEN ) {
         dbd_log( LOGSTD, "Volume pathname too long");
         exit(EXIT_FAILURE);        
     }
-    strncpy(dbpath, volinfo.v_dbpath, PATH_MAX - 9 - 1);
+    strncpy(dbpath, volinfo.v_dbpath, MAXPATHLEN - strlen("/.AppleDB"));
     strcat(dbpath, "/.AppleDB");
 
     /* Check or create dbpath */
@@ -412,7 +430,7 @@ int main(int argc, char **argv)
 
     /* Prepare upgrade ? */
     if (prep_upgrade) {
-        if (dbif_prep_upgrade(dbpath))
+        if (dbif_env_remove(dbpath))
             goto exit_failure;
         goto exit_success;
     }        
@@ -420,10 +438,16 @@ int main(int argc, char **argv)
     /* Check if -f is requested and wipe db if yes */
     if ((flags & DBD_FLAGS_FORCE) && rebuild && (volinfo.v_flags & AFPVOL_CACHE)) {
         char cmd[8 + MAXPATHLEN];
-        snprintf(cmd, 8 + MAXPATHLEN, "rm -f %s/*", dbpath);
+        close(lockfd);
+        snprintf(cmd, 8 + MAXPATHLEN, "rm -rf \"%s\"", dbpath);
         dbd_log( LOGDEBUG, "Removing old database of volume: '%s'", volpath);
         system(cmd);
+        if ((mkdir(dbpath, 0755)) != 0) {
+            dbd_log( LOGSTD, "Can't create dbpath \"%s\": %s", dbpath, strerror(errno));
+            exit(EXIT_FAILURE);
+        }
         dbd_log( LOGDEBUG, "Removed old database.");
+        lockfd = get_lock(dbpath);
     }
 
     /* 
@@ -445,11 +469,6 @@ int main(int argc, char **argv)
             dbif_close(dbd);
             goto exit_failure;
         }
-
-        if (dbd_stamp(dbd) < 0) {
-            dbif_close(dbd);
-            goto exit_failure;
-        }
     }
 
     /* Now execute given command scan|rebuild|dump */
@@ -464,9 +483,12 @@ int main(int argc, char **argv)
     }
 
     /* Cleanup */
-    if (! nocniddb && dbif_close(dbd) < 0) {
-        dbd_log( LOGSTD, "Error closing database");
-        goto exit_failure;
+    dbd_log(LOGDEBUG, "Closing db");
+    if (! nocniddb) {
+        if (dbif_close(dbd) < 0) {
+            dbd_log( LOGSTD, "Error closing database");
+            goto exit_failure;
+        }
     }
 
 exit_success:
index a1d51b67dcdd77dddf648997f7cdefef2faebd19..425fb400cd3f4d1e13a1b671b249278c1eba0c0a 100644 (file)
@@ -14,6 +14,7 @@ typedef unsigned int dbd_flags_t;
 #define DBD_FLAGS_FORCE    (1 << 1)
 #define DBD_FLAGS_EXCL     (1 << 2)
 #define DBD_FLAGS_CLEANUP  (1 << 3) /* Dont create AD stuff, but cleanup orphaned */
+#define DBD_FLAGS_STATS    (1 << 4)
 
 #define ADv2_DIRNAME ".AppleDouble"
 
@@ -25,8 +26,6 @@ typedef unsigned int dbd_flags_t;
 
 extern int nocniddb; /* Dont open CNID database, only scan filesystem */
 extern volatile sig_atomic_t alarmed;
-extern struct volinfo *volinfo;
-extern char cwdbuf[MAXPATHLEN+1];
 
 extern void dbd_log(enum logtype lt, char *fmt, ...);
 extern int cmd_dbd_scanvol(DBD *dbd, struct volinfo *volinfo, dbd_flags_t flags);
index d94c2224eeb03eae38a4df1f8d0f6f0afd0e3c3c..576df49fb570bd26009014e9ac8226d75eda5f6d 100644 (file)
@@ -1,6 +1,4 @@
 /*
-  $Id: cmd_dbd_scanvol.c,v 1.21 2010-04-11 07:01:23 franklahm Exp $
-
   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
 
   This program is free software; you can redistribute it and/or modify
@@ -35,6 +33,7 @@
 #include <atalk/volume.h>
 #include <atalk/ea.h>
 #include <atalk/util.h>
+#include <atalk/acl.h>
 
 #include "cmd_dbd.h"
 #include "dbif.h"
 #define ADDIR_OK (addir_ok == 0)
 #define ADFILE_OK (adfile_ok == 0)
 
-/* These must be accessible for cmd_dbd_* funcs */
-struct volinfo        *volinfo;
-char                  cwdbuf[MAXPATHLEN+1];
 
-/* Some static vars */
+static struct volinfo *myvolinfo;
+static char           cwdbuf[MAXPATHLEN+1];
 static DBD            *dbd;
 static DBD            *dbd_rebuild;
 static dbd_flags_t    dbd_flags;
@@ -59,6 +56,10 @@ static char           *netatalk_dirs[] = {
     ".AppleDesktop",
     NULL
 };
+static char           *special_dirs[] = {
+    ".zfs",
+    NULL
+};
 static struct cnid_dbd_rqst rqst;
 static struct cnid_dbd_rply rply;
 static jmp_buf jmp;
@@ -82,22 +83,22 @@ static char *utompath(char *upath)
     u = upath;
     outlen = strlen(upath);
 
-    if ((volinfo->v_casefold & AFPVOL_UTOMUPPER))
+    if ((myvolinfo->v_casefold & AFPVOL_UTOMUPPER))
         flags |= CONV_TOUPPER;
-    else if ((volinfo->v_casefold & AFPVOL_UTOMLOWER))
+    else if ((myvolinfo->v_casefold & AFPVOL_UTOMLOWER))
         flags |= CONV_TOLOWER;
 
-    if ((volinfo->v_flags & AFPVOL_EILSEQ)) {
+    if ((myvolinfo->v_flags & AFPVOL_EILSEQ)) {
         flags |= CONV__EILSEQ;
     }
 
     /* convert charsets */
-    if ((size_t)-1 == ( outlen = convert_charset(volinfo->v_volcharset,
+    if ((size_t)-1 == ( outlen = convert_charset(myvolinfo->v_volcharset,
                                                  CH_UTF8_MAC,
-                                                 volinfo->v_maccharset,
+                                                 myvolinfo->v_maccharset,
                                                  u, outlen, mpath, MAXPATHLEN, &flags)) ) {
         dbd_log( LOGSTD, "Conversion from %s to %s for %s failed.",
-                 volinfo->v_volcodepage, volinfo->v_maccodepage, u);
+                 myvolinfo->v_volcodepage, myvolinfo->v_maccodepage, u);
         return NULL;
     }
 
@@ -123,17 +124,17 @@ static char *mtoupath(char *mpath)
     }
 
     /* set conversion flags */
-    if (!(volinfo->v_flags & AFPVOL_NOHEX))
+    if (!(myvolinfo->v_flags & AFPVOL_NOHEX))
         flags |= CONV_ESCAPEHEX;
-    if (!(volinfo->v_flags & AFPVOL_USEDOTS))
+    if (!(myvolinfo->v_flags & AFPVOL_USEDOTS))
         flags |= CONV_ESCAPEDOTS;
 
-    if ((volinfo->v_casefold & AFPVOL_MTOUUPPER))
+    if ((myvolinfo->v_casefold & AFPVOL_MTOUUPPER))
         flags |= CONV_TOUPPER;
-    else if ((volinfo->v_casefold & AFPVOL_MTOULOWER))
+    else if ((myvolinfo->v_casefold & AFPVOL_MTOULOWER))
         flags |= CONV_TOLOWER;
 
-    if ((volinfo->v_flags & AFPVOL_EILSEQ)) {
+    if ((myvolinfo->v_flags & AFPVOL_EILSEQ)) {
         flags |= CONV__EILSEQ;
     }
 
@@ -144,11 +145,11 @@ static char *mtoupath(char *mpath)
     outlen = MAXPATHLEN;
 
     if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC,
-                                                volinfo->v_volcharset,
-                                                volinfo->v_maccharset,
+                                                myvolinfo->v_volcharset,
+                                                myvolinfo->v_maccharset,
                                                 m, inplen, u, outlen, &flags)) ) {
         dbd_log( LOGSTD, "conversion from UTF8-MAC to %s for %s failed.",
-                 volinfo->v_volcodepage, mpath);
+                 myvolinfo->v_volcodepage, mpath);
         return NULL;
     }
 
@@ -221,8 +222,8 @@ static int check_symlink(const char *name, int *adflags)
       and can compare it with the currents volume path
     */
     int i = 0;
-    while (volinfo->v_path[i]) {
-        if ((pathbuf[i] == 0) || (volinfo->v_path[i] != pathbuf[i])) {
+    while (myvolinfo->v_path[i]) {
+        if ((pathbuf[i] == 0) || (myvolinfo->v_path[i] != pathbuf[i])) {
             dbd_log( LOGDEBUG, "extra-share symlink '%s/%s', following", cwdbuf, name);
             return 1;
         }
@@ -271,6 +272,21 @@ static const char *check_netatalk_dirs(const char *name)
     return NULL;
 }
 
+/*
+  Check for special names
+  Returns pointer to name or NULL.
+*/
+static const char *check_special_dirs(const char *name)
+{
+    int c;
+
+    for (c=0; special_dirs[c]; c++) {
+        if ((strcmp(name, special_dirs[c])) == 0)
+            return special_dirs[c];
+    }
+    return NULL;
+}
+
 /*
   Check for .AppleDouble file, create if missing
 */
@@ -288,7 +304,7 @@ static int check_adfile(const char *fname, const struct stat *st)
     else
         adflags = ADFLAGS_DIR;
 
-    adname = volinfo->ad_path(fname, adflags);
+    adname = myvolinfo->ad_path(fname, adflags);
 
     if ((ret = access( adname, F_OK)) != 0) {
         if (errno != ENOENT) {
@@ -304,7 +320,7 @@ static int check_adfile(const char *fname, const struct stat *st)
             return -1;
 
         /* Create ad file */
-        ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
+        ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
 
         if ((ret = ad_open_metadata( fname, adflags, O_CREAT, &ad)) != 0) {
             dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s",
@@ -324,7 +340,7 @@ static int check_adfile(const char *fname, const struct stat *st)
         chmod(adname, st->st_mode);
 #endif
     } else {
-        ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
+        ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
         if (ad_open_metadata( fname, adflags, O_RDONLY, &ad) != 0) {
             dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s'", cwdbuf, fname);
             return -1;
@@ -454,10 +470,10 @@ static int check_addir(int volroot)
     }
 
     /* Check for ".Parent" */
-    if ( (adpar_ok = access(volinfo->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) {
+    if ( (adpar_ok = access(myvolinfo->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) {
         if (errno != ENOENT) {
             dbd_log(LOGSTD, "Access error on '%s/%s': %s",
-                    cwdbuf, volinfo->ad_path(".", ADFLAGS_DIR), strerror(errno));
+                    cwdbuf, myvolinfo->ad_path(".", ADFLAGS_DIR), strerror(errno));
             return -1;
         }
         dbd_log(LOGSTD, "Missing .AppleDouble/.Parent for '%s'", cwdbuf);
@@ -476,7 +492,7 @@ static int check_addir(int volroot)
         }
 
         /* Create ad dir and set name */
-        ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
+        ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
 
         if (ad_open_metadata( ".", ADFLAGS_DIR, O_CREAT, &ad) != 0) {
             dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno));
@@ -497,7 +513,7 @@ static int check_addir(int volroot)
             return -1;
         }
         chown(ADv2_DIRNAME, st.st_uid, st.st_gid);
-        chown(volinfo->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
+        chown(myvolinfo->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
     }
 
     return 0;
@@ -516,7 +532,7 @@ static int check_eafile_in_adouble(const char *name)
     char *namep, *namedup = NULL;
 
     /* Check if this is an AFPVOL_EA_AD vol */
-    if (volinfo->v_vfs_ea == AFPVOL_EA_AD) {
+    if (myvolinfo->v_vfs_ea == AFPVOL_EA_AD) {
         /* Does the filename contain "::EA" ? */
         namedup = strdup(name);
         if ((namep = strstr(namedup, "::EA")) == NULL) {
@@ -650,6 +666,8 @@ static int read_addir(void)
 /*
   Check CNID for a file/dir, both from db and from ad-file.
   For detailed specs see intro.
+
+  @return Correct CNID of object or CNID_INVALID (ie 0) on error
 */
 static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok, int adflags)
 {
@@ -657,17 +675,28 @@ static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfi
     cnid_t db_cnid, ad_cnid;
     struct adouble ad;
 
+    /* Force checkout every X items */
+    static int cnidcount = 0;
+    cnidcount++;
+    if (cnidcount > 10000) {
+        cnidcount = 0;
+        if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
+            dbd_log(LOGSTD, "Error checkpointing!");
+            return CNID_INVALID;
+        }
+    }
+
     /* Get CNID from ad-file if volume is using AFPVOL_CACHE */
     ad_cnid = 0;
-    if ( (volinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
-        ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
+    if ( (myvolinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
+        ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
         if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
             
             if (dbd_flags & DBD_FLAGS_CLEANUP)
-                return 0;
+                return CNID_INVALID;
 
             dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
-            return 0;
+            return CNID_INVALID;
         }
 
         if (dbd_flags & DBD_FLAGS_FORCE) {
@@ -680,7 +709,7 @@ static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfi
             ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, did, stamp);
 
         if (ad_cnid == 0)
-            dbd_log( LOGSTD, "Incorrect CNID data in .AppleDouble data for '%s/%s' (bad stamp?)", cwdbuf, name);
+            dbd_log( LOGSTD, "Bad CNID in adouble file of '%s/%s'", cwdbuf, name);
         else
             dbd_log( LOGDEBUG, "CNID from .AppleDouble file for '%s/%s': %u", cwdbuf, name, ntohl(ad_cnid));
 
@@ -694,7 +723,7 @@ static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfi
     memset(&rply, 0, sizeof(struct cnid_dbd_rply));
     rqst.did = did;
     rqst.cnid = ad_cnid;
-    if ( ! (volinfo->v_flags & AFPVOL_NODEV))
+    if ( ! (myvolinfo->v_flags & AFPVOL_NODEV))
         rqst.dev = st->st_dev;
     rqst.ino = st->st_ino;
     rqst.type = S_ISDIR(st->st_mode)?1:0;
@@ -703,7 +732,8 @@ static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfi
 
     /* Query the database */
     ret = dbd_lookup(dbd, &rqst, &rply, (dbd_flags & DBD_FLAGS_SCAN) ? 1 : 0);
-    dbif_txn_close(dbd, ret);
+    if (dbif_txn_close(dbd, ret) != 0)
+        return CNID_INVALID;
     if (rply.result == CNID_DBD_RES_OK) {
         db_cnid = rply.cnid;
     } else if (rply.result == CNID_DBD_RES_NOTFOUND) {
@@ -725,25 +755,60 @@ static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfi
         if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
             rqst.cnid = db_cnid;
             ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
-            dbif_txn_close(dbd, ret);
+            if (dbif_txn_close(dbd, ret) != 0)
+                return CNID_INVALID;
 
             rqst.cnid = ad_cnid;
             ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
-            dbif_txn_close(dbd, ret);
+            if (dbif_txn_close(dbd, ret) != 0)
+                return CNID_INVALID;
 
             ret = dbd_rebuild_add(dbd, &rqst, &rply);
-            dbif_txn_close(dbd, ret);
+            if (dbif_txn_close(dbd, ret) != 0)
+                return CNID_INVALID;
         }
         return ad_cnid;
     } else if (ad_cnid && (db_cnid == 0)) {
         /* in ad-file but not in db */
         if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
-            dbd_log( LOGDEBUG, "CNID rebuild add for '%s/%s', adding with CNID from ad-file: %u", cwdbuf, name, ntohl(ad_cnid));
+            /* Ensure the cnid from the ad-file is not already occupied by another file */
+            dbd_log(LOGDEBUG, "Checking whether CNID %u from ad-file is occupied",
+                    ntohl(ad_cnid));
+
+            rqst.cnid = ad_cnid;
+            ret = dbd_resolve(dbd, &rqst, &rply);
+            if (ret == CNID_DBD_RES_OK) {
+                /* Occupied! Choose another, update ad-file */
+                ret = dbd_add(dbd, &rqst, &rply, 1);
+                if (dbif_txn_close(dbd, ret) != 0)
+                    return CNID_INVALID;
+                db_cnid = rply.cnid;
+                dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
+
+                if ((myvolinfo->v_flags & AFPVOL_CACHE)
+                    && ADFILE_OK
+                    && ( ! (dbd_flags & DBD_FLAGS_SCAN))) {
+                    dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
+                            cwdbuf, name, ntohl(db_cnid));
+                    ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
+                    if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
+                        dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
+                                cwdbuf, name, strerror(errno));
+                        return CNID_INVALID;
+                    }
+                    ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
+                    ad_flush(&ad);
+                    ad_close_metadata(&ad);
+                }
+                return db_cnid;
+            }
+
+            dbd_log(LOGDEBUG, "CNID rebuild add '%s/%s' with CNID from ad-file %u",
+                    cwdbuf, name, ntohl(ad_cnid));
             rqst.cnid = ad_cnid;
-            ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
-            dbif_txn_close(dbd, ret);
             ret = dbd_rebuild_add(dbd, &rqst, &rply);
-            dbif_txn_close(dbd, ret);
+            if (dbif_txn_close(dbd, ret) != 0)
+                return CNID_INVALID;
         }
         return ad_cnid;
     } else if ((db_cnid == 0) && (ad_cnid == 0)) {
@@ -752,21 +817,24 @@ static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfi
         if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
             /* add to db */
             ret = dbd_add(dbd, &rqst, &rply, 1);
-            dbif_txn_close(dbd, ret);
+            if (dbif_txn_close(dbd, ret) != 0)
+                return CNID_INVALID;
             db_cnid = rply.cnid;
-            dbd_log( LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
+            dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
         }
     }
 
     if ((ad_cnid == 0) && db_cnid) {
         /* in db but zeroID in ad-file, write it to ad-file if AFPVOL_CACHE */
-        if ((volinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
+        if ((myvolinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
             if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
-                dbd_log( LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file", cwdbuf, name, ntohl(db_cnid));
-                ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
+                dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
+                        cwdbuf, name, ntohl(db_cnid));
+                ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
                 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
-                    dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
-                    return 0;
+                    dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
+                            cwdbuf, name, strerror(errno));
+                    return CNID_INVALID;
                 }
                 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
                 ad_flush(&ad);
@@ -776,7 +844,7 @@ static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfi
         return db_cnid;
     }
 
-    return 0;
+    return CNID_INVALID;
 }
 
 /*
@@ -828,12 +896,21 @@ static int dbd_readdir(int volroot, cnid_t did)
             continue;
         }
 
+        /* Check for special folders in volume root e.g. ".zfs" */
+        if (volroot) {
+            if ((name = check_special_dirs(ep->d_name)) != NULL) {
+                dbd_log(LOGSTD, "Ignoring special dir \"%s\"", name);
+                continue;
+            }
+        }
+
         /* Skip .AppleDouble dir in this loop */
         if (STRCMP(ep->d_name, == , ADv2_DIRNAME))
             continue;
 
         if ((ret = lstat(ep->d_name, &st)) < 0) {
-            dbd_log( LOGSTD, "Lost file while reading dir '%s/%s', probably removed: %s", cwdbuf, ep->d_name, strerror(errno));
+            dbd_log( LOGSTD, "Lost file while reading dir '%s/%s', probably removed: %s",
+                     cwdbuf, ep->d_name, strerror(errno));
             continue;
         }
         
@@ -864,6 +941,22 @@ static int dbd_readdir(int volroot, cnid_t did)
             continue;
         }
 
+        /**************************************************************************
+           Statistics
+         **************************************************************************/
+        static unsigned long long statcount = 0;
+        static time_t t = 0;
+
+        if (t == 0)
+            t = time(NULL);
+
+        statcount++;
+        if ((statcount % 10000) == 0) {
+            if (dbd_flags & DBD_FLAGS_STATS)            
+                dbd_log(LOGSTD, "Scanned: %10llu, time: %10llu s",
+                        statcount, (unsigned long long)(time(NULL) - t));
+        }
+
         /**************************************************************************
            Tests
         **************************************************************************/
@@ -885,19 +978,30 @@ static int dbd_readdir(int volroot, cnid_t did)
             cnid = check_cnid(ep->d_name, did, &st, adfile_ok, adflags);
 
             /* Now add this object to our rebuild dbd */
-            if (cnid) {
+            if (cnid && dbd_rebuild) {
+                static uint count = 0;
                 rqst.cnid = rply.cnid;
-                dbd_rebuild_add(dbd_rebuild, &rqst, &rply);
+                ret = dbd_rebuild_add(dbd_rebuild, &rqst, &rply);
+                if (dbif_txn_close(dbd_rebuild, ret) != 0)
+                    return -1;
                 if (rply.result != CNID_DBD_RES_OK) {
-                    dbd_log( LOGDEBUG, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db",
+                    dbd_log( LOGSTD, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db",
                              cnid, cwdbuf, ep->d_name);
-                    longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
+                    return -1;
+                }
+                count++;
+                if (count == 10000) {
+                    if (dbif_txn_checkpoint(dbd_rebuild, 0, 0, 0) < 0) {
+                        dbd_log(LOGSTD, "Error checkpointing!");
+                        return -1;
+                    }
+                    count = 0;
                 }
             }
         }
 
         /* Check EA files */
-        if (volinfo->v_vfs_ea == AFPVOL_EA_AD)
+        if (myvolinfo->v_vfs_ea == AFPVOL_EA_AD)
             check_eafiles(ep->d_name);
 
         /**************************************************************************
@@ -923,7 +1027,7 @@ static int dbd_readdir(int volroot, cnid_t did)
             close(cwd);
             *(strrchr(cwdbuf, '/')) = 0;
             if (ret < 0)
-                continue;
+                return -1;
         }
     }
 
@@ -944,22 +1048,22 @@ static int scanvol(struct volinfo *vi, dbd_flags_t flags)
     }
 
     /* Make this stuff accessible from all funcs easily */
-    volinfo = vi;
+    myvolinfo = vi;
     dbd_flags = flags;
 
     /* Init a fake struct vol with just enough so we can call ea_open and friends */
     volume.v_adouble = AD_VERSION2;
-    volume.v_vfs_ea = volinfo->v_vfs_ea;
+    volume.v_vfs_ea = myvolinfo->v_vfs_ea;
     initvol_vfs(&volume);
 
     /* Run with umask 0 */
     umask(0);
 
     /* Remove trailing slash from volume, chdir to vol */
-    if (volinfo->v_path[strlen(volinfo->v_path) - 1] == '/')
-        volinfo->v_path[strlen(volinfo->v_path) - 1] = 0;
-    strcpy(cwdbuf, volinfo->v_path);
-    chdir(volinfo->v_path);
+    if (myvolinfo->v_path[strlen(myvolinfo->v_path) - 1] == '/')
+        myvolinfo->v_path[strlen(myvolinfo->v_path) - 1] = 0;
+    strcpy(cwdbuf, myvolinfo->v_path);
+    chdir(myvolinfo->v_path);
 
     /* Start recursion */
     if (dbd_readdir(1, htonl(2)) < 0)  /* 2 = volumeroot CNID */
@@ -973,11 +1077,13 @@ static int scanvol(struct volinfo *vi, dbd_flags_t flags)
 */
 static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags)
 {
-    int ret, deleted = 0;
+    int ret = 0, deleted = 0;
     cnid_t dbd_cnid = 0, rebuild_cnid = 0;
     struct cnid_dbd_rqst rqst;
     struct cnid_dbd_rply rply;
 
+    dbd->db_param.txn_frequency = 0;
+
     /* jump over rootinfo key */
     if ( dbif_idwalk(dbd, &dbd_cnid, 0) != 1)
         return;
@@ -990,8 +1096,11 @@ static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags)
 
     /* Start main loop through dbd: get CNID from dbd */
     while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
+        /* Check if we got a termination signal */
+        if (alarmed)
+            longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
 
-        if (deleted > 50) {
+        if (deleted > 1000) {
             deleted = 0;
             if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
                 dbd_log(LOGSTD, "Error checkpointing!");
@@ -1011,10 +1120,19 @@ static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags)
                     dbd_log(LOGSTD, "Orphaned CNID in database: %u", dbd_cnid);
                     if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
                         rqst.cnid = htonl(dbd_cnid);
-                        ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
-                        dbif_txn_close(dbd, ret);
+                        if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) {
+                            dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid);
+                            (void)dbif_txn_abort(dbd);
+                            goto cleanup;
+                        }
+                        
+                        if (dbif_txn_close(dbd, ret) != 0)
+                            return;
                         deleted++;
                     }
+                    /* Check if we got a termination signal */
+                    if (alarmed)
+                        longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
                 }
                 return;
             } else
@@ -1024,26 +1142,32 @@ static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags)
 
         if (dbd_cnid < rebuild_cnid) {
             /* CNID is orphaned -> delete */
-            dbd_log(LOGSTD, "Orphaned CNID in database: %u.", dbd_cnid);
+            dbd_log(LOGSTD, "One orphaned CNID in database: %u.", dbd_cnid);
             if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
                 rqst.cnid = htonl(dbd_cnid);
-                ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
-                dbif_txn_close(dbd, ret);
+                if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) {
+                    dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid);
+                    (void)dbif_txn_abort(dbd);
+                    goto cleanup;
+                }
+                if (dbif_txn_close(dbd, ret) != 0)
+                    return;
                 deleted++;
             }
             continue;
         }
 
         if (dbd_cnid > rebuild_cnid) {
+            dbif_idwalk(dbd, NULL, 1); /* Close cursor */
+            dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
+            (void)dbif_txn_close(dbd, 2);
+            (void)dbif_txn_close(dbd_rebuild, 2);                
             dbd_log(LOGSTD, "Ghost CNID: %u. This is fatal! Dumping rebuild db:\n", rebuild_cnid);
             dbif_dump(dbd_rebuild, 0);
             dbd_log(LOGSTD, "Send this dump and a `dbd -d ...` dump to the Netatalk Dev team!");
-            dbif_txn_close(dbd, ret);
-            dbif_idwalk(dbd, NULL, 1); /* Close cursor */
-            dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
             goto cleanup;
         }
-    }
+    } /* while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) */
 
 cleanup:
     dbif_idwalk(dbd, NULL, 1); /* Close cursor */
@@ -1051,58 +1175,110 @@ cleanup:
     return;
 }
 
+static const char *get_tmpdb_path(void)
+{
+    pid_t pid = getpid();
+    static char path[MAXPATHLEN];
+    snprintf(path, MAXPATHLEN, "/tmp/tmpdb-dbd.%u", pid);
+    if (mkdir(path, 0755) != 0)
+        return NULL;
+    return path;
+}
+
 /*
   Main func called from cmd_dbd.c
 */
-int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *volinfo, dbd_flags_t flags)
+int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *vi, dbd_flags_t flags)
 {
     int ret = 0;
     struct db_param db_param = { 0 };
+    const char *tmpdb_path = NULL;
 
     /* Set cachesize for in-memory rebuild db */
-    db_param.cachesize = 128 * 1024 * 1024; /* 128 MB */
+    db_param.cachesize = 64 * 1024;         /* 64 MB */
+    db_param.maxlocks = DEFAULT_MAXLOCKS;
+    db_param.maxlockobjs = DEFAULT_MAXLOCKOBJS;
+    db_param.txn_frequency = 1000;          /* close txn every 1000 objects */
+    db_param.logfile_autoremove = 1;
 
     /* Make it accessible for all funcs */
     dbd = dbd_ref;
 
     /* We only support unicode volumes ! */
-    if ( volinfo->v_volcharset != CH_UTF8) {
-        dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", volinfo->v_volcodepage, volinfo->v_volcharset, CH_UTF8);
+    if ( vi->v_volcharset != CH_UTF8) {
+        dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", vi->v_volcodepage, vi->v_volcharset, CH_UTF8);
         return -1;
     }
 
-    if (! nocniddb) {
+    /* temporary rebuild db, used with -re rebuild to delete unused CNIDs, not used with -f */
+    if (! nocniddb && !(flags & DBD_FLAGS_FORCE)) {
         /* Get volume stamp */
         dbd_getstamp(dbd, &rqst, &rply);
         if (rply.result != CNID_DBD_RES_OK)
-            goto exit_cleanup;
+            goto exit;
         memcpy(stamp, rply.name, CNID_DEV_LEN);
 
         /* open/create rebuild dbd, copy rootinfo key */
-        if (NULL == (dbd_rebuild = dbif_init(NULL, NULL)))
-            return -1;
-        if (0 != (dbif_open(dbd_rebuild, &db_param, 0)))
-            return -1;
-        if (0 != (dbif_copy_rootinfokey(dbd, dbd_rebuild)))
-            goto exit_cleanup;
+        tmpdb_path = get_tmpdb_path();
+        if (NULL == (dbd_rebuild = dbif_init(tmpdb_path, "cnid2.db"))) {
+            ret = -1;
+            goto exit;
+        }
+
+        if (dbif_env_open(dbd_rebuild,
+                          &db_param,
+                          DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN) < 0) {
+            dbd_log(LOGSTD, "error opening tmp database!");
+            goto exit;
+        }
+
+        if (0 != (dbif_open(dbd_rebuild, NULL, 0))) {
+            ret = -1;
+            goto exit;
+        }
+
+        if (0 != (dbif_copy_rootinfokey(dbd, dbd_rebuild))) {
+            ret = -1;
+            goto exit;
+        }
     }
 
-    if (setjmp(jmp) != 0)
-        goto exit_cleanup;      /* Got signal, jump from dbd_readdir */
+    if (setjmp(jmp) != 0) {
+        ret = 0;                /* Got signal, jump from dbd_readdir */
+        goto exit;
+    }
 
     /* scanvol */
-    if ( (scanvol(volinfo, flags)) != 0)
-        return -1;
+    if ( (scanvol(vi, flags)) != 0) {
+        ret = -1;
+        goto exit;
+    }
 
+exit:
     if (! nocniddb) {
-        /* We can only do this in exclusive mode, otherwise we might delete CNIDs added from
-           other clients in between our pass 1 and 2 */
-        if (flags & DBD_FLAGS_EXCL)
+        if (dbif_txn_close(dbd, 2) != 0)
+            ret = -1;
+        if (dbd_rebuild)
+            if (dbif_txn_close(dbd_rebuild, 2) != 0)
+                ret = -1;
+        if ((ret == 0) && dbd_rebuild && (flags & DBD_FLAGS_EXCL) && !(flags & DBD_FLAGS_FORCE))
+            /* We can only do this in exclusive mode, otherwise we might delete CNIDs added from
+               other clients in between our pass 1 and 2 */
             delete_orphaned_cnids(dbd, dbd_rebuild, flags);
     }
 
-exit_cleanup:
-    if (! nocniddb)
+    if (dbd_rebuild) {
+        dbd_log(LOGDEBUG, "Closing tmp db");
         dbif_close(dbd_rebuild);
+
+        if (tmpdb_path) {
+            char cmd[8 + MAXPATHLEN];
+            snprintf(cmd, 8 + MAXPATHLEN, "rm -f %s/*", tmpdb_path);
+            dbd_log( LOGDEBUG, "Removing temp database '%s'", tmpdb_path);
+            system(cmd);
+            snprintf(cmd, 8 + MAXPATHLEN, "rmdir %s", tmpdb_path);
+            system(cmd);
+        }        
+    }
     return ret;
 }
index 6fda59e95c96afa930cc8a0a09d0169ed573fb3f..a51d15c4196d911f2f132cc42207cc6f87fddcb2 100644 (file)
@@ -1,9 +1,8 @@
 /*
- * $Id: cnid_metad.c,v 1.22 2009-11-16 02:04:47 didg Exp $
- *
  * Copyright (C) Joerg Lenneis 2003
- * All Rights Reserved.  See COPYING.
+ * Copyright (C) Frank Lahm 2009, 2010
  *
+ * All Rights Reserved.  See COPYING.
  */
 
 /* 
@@ -22,6 +21,8 @@
    Result:
                        via TCP socket
    4.       afpd          ------->         cnid_dbd
+
+   cnid_metad and cnid_dbd have been converted to non-blocking IO in 2010.
  */
 
 
 #include <errno.h>
 #include <string.h>
 #include <signal.h>
-#ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
-#endif
-#ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
-#endif
-#ifdef HAVE_SYS_WAIT_H
 #include <sys/wait.h>
-#endif
-#ifdef HAVE_SYS_UIO_H
 #include <sys/uio.h>
-#endif
 #include <sys/un.h>
 #define _XPG4_2 1
 #include <sys/socket.h>
 #include <stdio.h>
 #include <time.h>
-#include <sys/ioctl.h>
 
 #ifndef WEXITSTATUS
 #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
@@ -95,8 +87,9 @@
 #include <atalk/util.h>
 #include <atalk/logger.h>
 #include <atalk/cnid_dbd_private.h>
+#include <atalk/paths.h>
+#include <atalk/volinfo.h>
 
-#include "db_param.h"
 #include "usockfd.h"
 
 #define DBHOME        ".AppleDB"
@@ -114,7 +107,7 @@ static volatile sig_atomic_t sigchild = 0;
 #define DEFAULTPORT  "4700"
 
 struct server {
-    char  *name;
+    struct volinfo *volinfo;
     pid_t pid;
     time_t tm;                    /* When respawned last */
     int count;                    /* Times respawned in the last TESTTIME secondes */
@@ -126,72 +119,38 @@ static struct server srv[MAXVOLS];
 /* Default logging config: log to syslog with level log_note */
 static char logconfig[MAXPATHLEN + 21 + 1] = "default log_note";
 
-static struct server *test_usockfn(char *dir)
+static void daemon_exit(int i)
 {
-    int i;
-    for (i = 0; i < MAXVOLS; i++) {
-        if (srv[i].name && !strcmp(srv[i].name, dir)) {
-            return &srv[i];
-        }
-    }
-    return NULL;
+    server_unlock(_PATH_CNID_METAD_LOCK);
+    exit(i);
 }
 
-/* -------------------- */
-static int send_cred(int socket, int fd)
+/* ------------------ */
+static void sigterm_handler(int sig)
 {
-    int ret;
-    struct msghdr msgh;
-    struct iovec iov[1];
-    struct cmsghdr *cmsgp = NULL;
-    char *buf;
-    size_t size;
-    int er=0;
-
-    size = CMSG_SPACE(sizeof fd);
-    buf = malloc(size);
-    if (!buf) {
-        LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));
-        return -1;
+    switch( sig ) {
+    case SIGTERM :
+        LOG(log_info, logtype_afpd, "shutting down on signal %d", sig );
+        break;
+    default :
+        LOG(log_error, logtype_afpd, "unexpected signal: %d", sig);
     }
+    daemon_exit(0);
+}
 
-    memset(&msgh,0,sizeof (msgh));
-    memset(buf,0, size);
-
-    msgh.msg_name = NULL;
-    msgh.msg_namelen = 0;
-
-    msgh.msg_iov = iov;
-    msgh.msg_iovlen = 1;
-
-    iov[0].iov_base = &er;
-    iov[0].iov_len = sizeof(er);
-
-    msgh.msg_control = buf;
-    msgh.msg_controllen = size;
-
-    cmsgp = CMSG_FIRSTHDR(&msgh);
-    cmsgp->cmsg_level = SOL_SOCKET;
-    cmsgp->cmsg_type = SCM_RIGHTS;
-    cmsgp->cmsg_len = CMSG_LEN(sizeof(fd));
-
-    *((int *)CMSG_DATA(cmsgp)) = fd;
-    msgh.msg_controllen = cmsgp->cmsg_len;
-
-    do  {
-        ret = sendmsg(socket,&msgh, 0);
-    } while ( ret == -1 && errno == EINTR );
-    if (ret == -1) {
-        LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));
-        free(buf);
-        return -1;
+static struct server *test_usockfn(struct volinfo *volinfo)
+{
+    int i;
+    for (i = 0; i < MAXVOLS; i++) {
+        if ((srv[i].volinfo) && (strcmp(srv[i].volinfo->v_path, volinfo->v_path) == 0)) {
+            return &srv[i];
+        }
     }
-    free(buf);
-    return 0;
+    return NULL;
 }
 
 /* -------------------- */
-static int maybe_start_dbd(char *dbdpn, char *dbdir, char *usockfn)
+static int maybe_start_dbd(char *dbdpn, struct volinfo *volinfo)
 {
     pid_t pid;
     struct server *up;
@@ -200,31 +159,32 @@ static int maybe_start_dbd(char *dbdpn, char *dbdir, char *usockfn)
     time_t t;
     char buf1[8];
     char buf2[8];
+    char *volpath = volinfo->v_path;
 
-    LOG(log_maxdebug, logtype_cnid, "maybe_start_dbd: dbdir: '%s', UNIX socket file: '%s'", 
-        dbdir, usockfn);
+    LOG(log_debug, logtype_cnid, "maybe_start_dbd: Volume: \"%s\"", volpath);
 
-    up = test_usockfn(dbdir);
+    up = test_usockfn(volinfo);
     if (up && up->pid) {
         /* we already have a process, send our fd */
-        if (send_cred(up->control_fd, rqstfd) < 0) {
+        if (send_fd(up->control_fd, rqstfd) < 0) {
             /* FIXME */
             return -1;
         }
         return 0;
     }
 
-    LOG(log_maxdebug, logtype_cnid, "maybe_start_dbd: no cnid_dbd for that volume yet. Starting one ...");
+    LOG(log_maxdebug, logtype_cnid, "maybe_start_dbd: no cnid_dbd for that volume yet");
 
     time(&t);
     if (!up) {
         /* find an empty slot */
         for (i = 0; i < MAXVOLS; i++) {
-            if ( !srv[i].name ) {
+            if (srv[i].volinfo == NULL) {
                 up = &srv[i];
+                up->volinfo = volinfo;
+                retainvolinfo(volinfo);
                 up->tm = t;
                 up->count = 0;
-                up->name = strdup(dbdir);
                 break;
             }
         }
@@ -232,8 +192,7 @@ static int maybe_start_dbd(char *dbdpn, char *dbdir, char *usockfn)
             LOG(log_error, logtype_cnid, "no free slot for cnid_dbd child. Configured maximum: %d. Do you have so many volumes?", MAXVOLS);
             return -1;
         }
-    }
-    else {
+    } else {
         /* we have a slot but no process, check for respawn too fast */
         if ( (t < (up->tm + TESTTIME)) /* We're in the respawn time window */
              &&
@@ -293,16 +252,15 @@ static int maybe_start_dbd(char *dbdpn, char *dbdir, char *usockfn)
             /* there's a pb with the db inform child
              * it will run recover, delete the db whatever
              */
-            LOG(log_error, logtype_cnid, "try with -d %s", up->name);
-            ret = execlp(dbdpn, dbdpn, "-d", dbdir, buf1, buf2, logconfig, NULL);
+            LOG(log_error, logtype_cnid, "try with -d %s", up->volinfo->v_path);
+            ret = execlp(dbdpn, dbdpn, "-d", volpath, buf1, buf2, logconfig, NULL);
         }
         else {
-            ret = execlp(dbdpn, dbdpn, dbdir, buf1, buf2, logconfig, NULL);
-        }
-        if (ret < 0) {
-            LOG(log_error, logtype_cnid, "Fatal error in exec: %s", strerror(errno));
-            exit(0);
+            ret = execlp(dbdpn, dbdpn, volpath, buf1, buf2, logconfig, NULL);
         }
+        /* Yikes! We're still here, so exec failed... */
+        LOG(log_error, logtype_cnid, "Fatal error in exec: %s", strerror(errno));
+        daemon_exit(0);
     }
     /*
      *  Parent.
@@ -314,12 +272,12 @@ static int maybe_start_dbd(char *dbdpn, char *dbdir, char *usockfn)
 }
 
 /* ------------------ */
-static int set_dbdir(char *dbdir, int len)
+static int set_dbdir(char *dbdir)
 {
+    int len;
     struct stat st;
 
-    if (!len)
-        return -1;
+    len = strlen(dbdir);
 
     if (stat(dbdir, &st) < 0 && mkdir(dbdir, 0755) < 0) {
         LOG(log_error, logtype_cnid, "set_dbdir: mkdir failed for %s", dbdir);
@@ -388,15 +346,57 @@ static void set_signal(void)
     struct sigaction sv;
     sigset_t set;
 
-    signal(SIGPIPE, SIG_IGN);
+    memset(&sv, 0, sizeof(sv));
 
+    /* Catch SIGCHLD */
     sv.sa_handler = catch_child;
     sv.sa_flags = SA_NOCLDSTOP;
     sigemptyset(&sv.sa_mask);
     if (sigaction(SIGCHLD, &sv, NULL) < 0) {
         LOG(log_error, logtype_cnid, "cnid_metad: sigaction: %s", strerror(errno));
-        exit(1);
+        daemon_exit(EXITERR_SYS);
+    }
+
+    /* Catch SIGTERM */
+    sv.sa_handler = sigterm_handler;
+    sigfillset(&sv.sa_mask );
+    if (sigaction(SIGTERM, &sv, NULL ) < 0 ) {
+        LOG(log_error, logtype_afpd, "sigaction: %s", strerror(errno) );
+        daemon_exit(EXITERR_SYS);
+    }
+
+    /* Ignore the rest */
+    sv.sa_handler = SIG_IGN;
+    sigemptyset(&sv.sa_mask );
+    if (sigaction(SIGALRM, &sv, NULL ) < 0 ) {
+        LOG(log_error, logtype_afpd, "sigaction: %s", strerror(errno) );
+        daemon_exit(EXITERR_SYS);
     }
+    sv.sa_handler = SIG_IGN;
+    sigemptyset(&sv.sa_mask );
+    if (sigaction(SIGHUP, &sv, NULL ) < 0 ) {
+        LOG(log_error, logtype_afpd, "sigaction: %s", strerror(errno) );
+        daemon_exit(EXITERR_SYS);
+    }
+    sv.sa_handler = SIG_IGN;
+    sigemptyset(&sv.sa_mask );
+    if (sigaction(SIGUSR1, &sv, NULL ) < 0 ) {
+        LOG(log_error, logtype_afpd, "sigaction: %s", strerror(errno) );
+        daemon_exit(EXITERR_SYS);
+    }
+    sv.sa_handler = SIG_IGN;
+    sigemptyset(&sv.sa_mask );
+    if (sigaction(SIGUSR2, &sv, NULL ) < 0 ) {
+        LOG(log_error, logtype_afpd, "sigaction: %s", strerror(errno) );
+        daemon_exit(EXITERR_SYS);
+    }
+    sv.sa_handler = SIG_IGN;
+    sigemptyset(&sv.sa_mask );
+    if (sigaction(SIGPIPE, &sv, NULL ) < 0 ) {
+        LOG(log_error, logtype_afpd, "sigaction: %s", strerror(errno) );
+        daemon_exit(EXITERR_SYS);
+    }
+
     /* block everywhere but in pselect */
     sigemptyset(&set);
     sigaddset(&set, SIGCHLD);
@@ -406,14 +406,13 @@ static void set_signal(void)
 /* ------------------ */
 int main(int argc, char *argv[])
 {
-    char  dbdir[MAXPATHLEN + 1];
+    char  volpath[MAXPATHLEN + 1];
     int   len, actual_len;
     pid_t pid;
     int   status;
     char  *dbdpn = _PATH_CNID_DBD;
     char  *host = DEFAULTHOST;
     char  *port = DEFAULTPORT;
-    struct db_param *dbp;
     int    i;
     int    cc;
     uid_t  uid = 0;
@@ -424,6 +423,7 @@ int main(int argc, char *argv[])
     char   *loglevel = NULL;
     char   *logfile  = NULL;
     sigset_t set;
+    struct volinfo *volinfo;
 
     set_processname("cnid_metad");
 
@@ -480,54 +480,35 @@ int main(int argc, char *argv[])
 
     if (err) {
         LOG(log_error, logtype_cnid, "main: bad arguments");
-        exit(1);
+        daemon_exit(1);
     }
 
-    if (!debug) {
-
-        switch (fork()) {
-        case 0 :
-            fclose(stdin);
-            fclose(stdout);
-            fclose(stderr);
-
-#ifdef TIOCNOTTY
-            {
-                int i;
-                if (( i = open( "/dev/tty", O_RDWR )) >= 0 ) {
-                    (void)ioctl( i, TIOCNOTTY, 0 );
-                    setpgid( 0, getpid());
-                    (void) close(i);
-                }
-            }
-#else
-            setpgid( 0, getpid());
-#endif
-            break;
-        case -1 :  /* error */
-            LOG(log_error, logtype_cnid, "detach from terminal: %s", strerror(errno));
-            exit(1);
-        default :  /* server */
-            exit(0);
-        }
+    /* Check PID lockfile and become a daemon */
+    switch(server_lock("cnid_metad", _PATH_CNID_METAD_LOCK, debug)) {
+    case -1: /* error */
+        daemon_exit(EXITERR_SYS);
+    case 0: /* child */
+        break;
+    default: /* server */
+        exit(0);
     }
 
     if ((srvfd = tsockfd_create(host, port, 10)) < 0)
-        exit(1);
+        daemon_exit(1);
 
     /* switch uid/gid */
     if (uid || gid) {
-        LOG(log_info, logtype_cnid, "Setting uid/gid to %i/%i", uid, gid);
+        LOG(log_debug, logtype_cnid, "Setting uid/gid to %i/%i", uid, gid);
         if (gid) {
             if (SWITCH_TO_GID(gid) < 0) {
                 LOG(log_info, logtype_cnid, "unable to switch to group %d", gid);
-                exit(1);
+                daemon_exit(1);
             }
         }
         if (uid) {
             if (SWITCH_TO_UID(uid) < 0) {
                 LOG(log_info, logtype_cnid, "unable to switch to user %d", uid);
-                exit(1);
+                daemon_exit(1);
             }
         }
     }
@@ -562,9 +543,8 @@ int main(int argc, char *argv[])
         if (rqstfd <= 0)
             continue;
 
-        /* TODO: Check out read errors, broken pipe etc. in libatalk. Is
-           SIGIPE ignored there? Answer: Ignored for dsi, but not for asp ... */
-        ret = read(rqstfd, &len, sizeof(int));
+        ret = readt(rqstfd, &len, sizeof(int), 1, 4);
+
         if (!ret) {
             /* already close */
             goto loop_end;
@@ -586,7 +566,7 @@ int main(int argc, char *argv[])
             goto loop_end;
         }
 
-        actual_len = read(rqstfd, dbdir, len);
+        actual_len = readt(rqstfd, volpath, len, 1, 5);
         if (actual_len < 0) {
             LOG(log_severe, logtype_cnid, "Read(2) error : %s", strerror(errno));
             goto loop_end;
@@ -595,17 +575,22 @@ int main(int argc, char *argv[])
             LOG(log_error, logtype_cnid, "error/short read (dir): %s", strerror(errno));
             goto loop_end;
         }
-        dbdir[len] = '\0';
+        volpath[len] = '\0';
 
-        if (set_dbdir(dbdir, len) < 0) {
+        /* Load .volinfo file */
+        if ((volinfo = allocvolinfo(volpath)) == NULL) {
+            LOG(log_severe, logtype_cnid, "allocvolinfo(\"%s\"): %s",
+                volpath, strerror(errno));
             goto loop_end;
         }
 
-        if ((dbp = db_param_read(dbdir, METAD)) == NULL) {
-            LOG(log_error, logtype_cnid, "Error reading config file");
+        if (set_dbdir(volinfo->v_dbpath) < 0) {
             goto loop_end;
         }
-        maybe_start_dbd(dbdpn, dbdir, dbp->usock_file);
+
+        maybe_start_dbd(dbdpn, volinfo);
+
+        (void)closevolinfo(volinfo);
 
     loop_end:
         close(rqstfd);
index 04e43420707684dcc3f5c974bcac4605094ddf32..083d27a7d8022129c9c8f8ffb4f22571e5ce8842 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * $Id: comm.c,v 1.6 2009-10-19 08:09:07 didg Exp $
- *
  * Copyright (C) Joerg Lenneis 2003
+ * Copyright (C) Frank Lahm 2010
+ *
  * All Rights Reserved.  See COPYING.
  */
 
@@ -9,41 +9,32 @@
 #include "config.h"
 #endif
 
+#ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 600
+#endif
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__
+#endif
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-
-#ifdef HAVE_UNISTD_H
 #include <unistd.h>
-#endif
-
 #include <sys/param.h>
-#define _XPG4_2 1
-#include <sys/socket.h>
-
-#ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
-#endif
-
-#ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
-#endif
-
-#ifdef HAVE_SYS_UIO_H
 #include <sys/uio.h>
-#endif
-
-#ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
-#endif
-
 #include <sys/select.h>
-
 #include <assert.h>
 #include <time.h>
 
 #include <atalk/logger.h>
+#include <atalk/util.h>
 #include <atalk/cnid_dbd_private.h>
 
 #include "db_param.h"
@@ -87,51 +78,6 @@ static void invalidate_fd(int fd)
     return;
 }
 
-static int recv_cred(int fd)
-{
-    int ret;
-    struct msghdr msgh;
-    struct iovec iov[1];
-    struct cmsghdr *cmsgp = NULL;
-    char buf[CMSG_SPACE(sizeof(int))];
-    char dbuf[80];
-
-    memset(&msgh,0,sizeof(msgh));
-    memset(buf,0,sizeof(buf));
-
-    msgh.msg_name = NULL;
-    msgh.msg_namelen = 0;
-
-    msgh.msg_iov = iov;
-    msgh.msg_iovlen = 1;
-
-    iov[0].iov_base = dbuf;
-    iov[0].iov_len = sizeof(dbuf);
-
-    msgh.msg_control = buf;
-    msgh.msg_controllen = sizeof(buf);
-
-    do  {
-        ret = recvmsg(fd ,&msgh,0);
-    } while ( ret == -1 && errno == EINTR );
-
-    if ( ret == -1 ) {
-        return -1;
-    }
-
-    for ( cmsgp = CMSG_FIRSTHDR(&msgh); cmsgp != NULL; cmsgp = CMSG_NXTHDR(&msgh,cmsgp) ) {
-        if ( cmsgp->cmsg_level == SOL_SOCKET && cmsgp->cmsg_type == SCM_RIGHTS ) {
-            return *(int *) CMSG_DATA(cmsgp);
-        }
-    }
-
-    if ( ret == sizeof (int) )
-        errno = *(int *)dbuf; /* Rcvd errno */
-    else
-        errno = ENOENT;    /* Default errno */
-
-    return -1;
-}
 
 /*
  *  Check for client requests. We keep up to fd_table_size open descriptors in
@@ -181,7 +127,7 @@ static int check_fd(time_t timeout, const sigset_t *sigmask, time_t *now)
     if (FD_ISSET(control_fd, &readfds)) {
         int    l = 0;
 
-        fd = recv_cred(control_fd);
+        fd = recv_fd(control_fd, 0);
         if (fd < 0) {
             return -1;
         }
@@ -264,8 +210,17 @@ int comm_rcv(struct cnid_dbd_rqst *rqst, time_t timeout, const sigset_t *sigmask
 
     if (!cur_fd)
         return 0;
+
+    LOG(log_maxdebug, logtype_cnid, "comm_rcv: got data on fd %u", cur_fd);
+
+    if (setnonblock(cur_fd, 1) != 0) {
+        LOG(log_error, logtype_cnid, "comm_rcv: setnonblock: %s", strerror(errno));
+        return -1;
+    }
+
     nametmp = rqst->name;
-    if ((b = read(cur_fd, rqst, sizeof(struct cnid_dbd_rqst))) != sizeof(struct cnid_dbd_rqst)) {
+    if ((b = readt(cur_fd, rqst, sizeof(struct cnid_dbd_rqst), 1, CNID_DBD_TIMEOUT))
+        != sizeof(struct cnid_dbd_rqst)) {
         if (b)
             LOG(log_error, logtype_cnid, "error reading message header: %s", strerror(errno));
         invalidate_fd(cur_fd);
@@ -273,7 +228,8 @@ int comm_rcv(struct cnid_dbd_rqst *rqst, time_t timeout, const sigset_t *sigmask
         return 0;
     }
     rqst->name = nametmp;
-    if (rqst->namelen && read(cur_fd, rqst->name, rqst->namelen) != rqst->namelen) {
+    if (rqst->namelen && readt(cur_fd, rqst->name, rqst->namelen, 1, CNID_DBD_TIMEOUT)
+        != rqst->namelen) {
         LOG(log_error, logtype_cnid, "error reading message name: %s", strerror(errno));
         invalidate_fd(cur_fd);
         return 0;
@@ -282,6 +238,8 @@ int comm_rcv(struct cnid_dbd_rqst *rqst, time_t timeout, const sigset_t *sigmask
        needs zero terminated strings. */
     rqst->name[rqst->namelen] = '\0';
 
+    LOG(log_maxdebug, logtype_cnid, "comm_rcv: got %u bytes", b + rqst->namelen);
+
     return 1;
 }
 
index 614bd27da6adbeedbe166768f08fe811f6c49d7a..46d91fc18fab1c8e97c815e91393ea73c418426e 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: comm.h,v 1.5 2009-10-19 08:09:07 didg Exp $
- *
  * Copyright (C) Joerg Lenneis 2003
  * All Rights Reserved.  See COPYING.
  */
@@ -8,6 +6,8 @@
 #ifndef CNID_DBD_COMM_H
 #define CNID_DBD_COMM_H 1
 
+/* number of seconds to try reading in readt */
+#define CNID_DBD_TIMEOUT 1
 
 #include <atalk/cnid_dbd_private.h>
 
index 029c21b00124ecafca0615c6cb30f1e1978e2bb7..c89f8e5cec00890c2d9b4a3e8410241cc7b684a7 100644 (file)
 #define DB_PARAM_FN       "db_param"
 #define MAXKEYLEN         64
 
-#define DEFAULT_LOGFILE_AUTOREMOVE 1
-#define DEFAULT_CACHESIZE          (8 * 1024)
-#define DEFAULT_FLUSH_FREQUENCY    1000
-#define DEFAULT_FLUSH_INTERVAL     1800
-#define DEFAULT_USOCK_FILE         "usock"
-#define DEFAULT_FD_TABLE_SIZE      512
-#define DEFAULT_IDLE_TIMEOUT       (10 * 60)
-
 static struct db_param params;
 static int parse_err;
 
@@ -66,6 +58,8 @@ static void default_params(struct db_param *dbp, char *dir)
 {        
     dbp->logfile_autoremove  = DEFAULT_LOGFILE_AUTOREMOVE;
     dbp->cachesize           = DEFAULT_CACHESIZE;
+    dbp->maxlocks            = DEFAULT_MAXLOCKS;
+    dbp->maxlockobjs         = DEFAULT_MAXLOCKOBJS;
     dbp->flush_frequency     = DEFAULT_FLUSH_FREQUENCY;
     dbp->flush_interval      = DEFAULT_FLUSH_INTERVAL;
     if (make_pathname(dbp->usock_file, dir, DEFAULT_USOCK_FILE, usock_maxlen()) < 0) {
@@ -98,7 +92,7 @@ static int parse_int(char *val)
    buffer overflow) nor elegant, we need to add support for whitespace in
    filenames as well. */
 
-struct db_param *db_param_read(char *dir, enum identity id)
+struct db_param *db_param_read(char *dir)
 {
     FILE *fp;
     static char key[MAXKEYLEN + 1];
@@ -145,33 +139,32 @@ struct db_param *db_param_read(char *dir, enum identity id)
                 LOG(log_info, logtype_cnid, "db_param: setting UNIX domain socket filename to %s", params.usock_file);
         }
 
-        /* Config for cnid_metad only */
-        if ( id == METAD ) {
-            /* Currently empty */
+        if (! strcmp(key, "fd_table_size")) {
+            params.fd_table_size = parse_int(val);
+            LOG(log_info, logtype_cnid, "db_param: setting max number of concurrent afpd connections per volume (fd_table_size) to %d", params.fd_table_size);
+        } else if (! strcmp(key, "logfile_autoremove")) {
+            params.logfile_autoremove = parse_int(val);
+            LOG(log_info, logtype_cnid, "db_param: setting logfile_autoremove to %d", params.logfile_autoremove);
+        } else if (! strcmp(key, "cachesize")) {
+            params.cachesize = parse_int(val);
+            LOG(log_info, logtype_cnid, "db_param: setting cachesize to %d", params.cachesize);
+        } else if (! strcmp(key, "maxlocks")) {
+            params.maxlocks = parse_int(val);
+            LOG(log_info, logtype_cnid, "db_param: setting maxlocks to %d", params.maxlocks);
+        } else if (! strcmp(key, "maxlockobjs")) {
+            params.maxlockobjs = parse_int(val);
+            LOG(log_info, logtype_cnid, "db_param: setting maxlockobjs to %d", params.maxlockobjs);
+        } else if (! strcmp(key, "flush_frequency")) {
+            params.flush_frequency = parse_int(val);
+            LOG(log_info, logtype_cnid, "db_param: setting flush_frequency to %d", params.flush_frequency);
+        } else if (! strcmp(key, "flush_interval")) {
+            params.flush_interval = parse_int(val);
+            LOG(log_info, logtype_cnid, "db_param: setting flush_interval to %d", params.flush_interval);
+        } else if (! strcmp(key, "idle_timeout")) {
+            params.idle_timeout = parse_int(val);
+            LOG(log_info, logtype_cnid, "db_param: setting idle timeout to %d", params.idle_timeout);
         }
 
-        /* Config for dbd only */
-        else if (id == CNID_DBD ) {
-            if (! strcmp(key, "fd_table_size")) {
-                params.fd_table_size = parse_int(val);
-                LOG(log_info, logtype_cnid, "db_param: setting max number of concurrent afpd connections per volume (fd_table_size) to %d", params.fd_table_size);
-            } else if (! strcmp(key, "logfile_autoremove")) {
-                params.logfile_autoremove = parse_int(val);
-                LOG(log_info, logtype_cnid, "db_param: setting logfile_autoremove to %d", params.logfile_autoremove);
-            } else if (! strcmp(key, "cachesize")) {
-                params.cachesize = parse_int(val);
-                LOG(log_info, logtype_cnid, "db_param: setting cachesize to %d", params.cachesize);
-            } else if (! strcmp(key, "flush_frequency")) {
-                params.flush_frequency = parse_int(val);
-                LOG(log_info, logtype_cnid, "db_param: setting flush_frequency to %d", params.flush_frequency);
-            } else if (! strcmp(key, "flush_interval")) {
-                params.flush_interval = parse_int(val);
-                LOG(log_info, logtype_cnid, "db_param: setting flush_interval to %d", params.flush_interval);
-            } else if (! strcmp(key, "idle_timeout")) {
-                params.idle_timeout = parse_int(val);
-                LOG(log_info, logtype_cnid, "db_param: setting idle timeout to %d", params.idle_timeout);
-            }
-        }
         if (parse_err)
             break;
     }
index ebcea6b823185494b937d31a78df6a0974e36a10..302b16db93632b6f7617eeafbd635fb97926ea95 100644 (file)
@@ -1,7 +1,6 @@
 /*
- * $Id: db_param.h,v 1.6 2009-12-21 07:32:01 franklahm Exp $
- *
  * Copyright (C) Joerg Lenneis 2003
+ * Copyright (C) Frank Lahm 2010
  * All Rights Reserved.  See COPYING.
  */
 
 #include <sys/param.h>
 #include <sys/cdefs.h>
 
-enum identity {
-    METAD,
-    CNID_DBD
-};
+#define DEFAULT_LOGFILE_AUTOREMOVE 1
+#define DEFAULT_CACHESIZE          (8 * 1024) /* KB, so 8 MB */
+#define DEFAULT_MAXLOCKS           5000
+#define DEFAULT_MAXLOCKOBJS        5000
+#define DEFAULT_FLUSH_FREQUENCY    1000
+#define DEFAULT_FLUSH_INTERVAL     1800
+#define DEFAULT_USOCK_FILE         "usock"
+#define DEFAULT_FD_TABLE_SIZE      512
+#define DEFAULT_IDLE_TIMEOUT       (10 * 60)
 
 struct db_param {
     char *dir;
     int logfile_autoremove;
     int cachesize;              /* in KB */
+    int maxlocks;
+    int maxlockobjs;
     int flush_interval;
     int flush_frequency;
+    int txn_frequency;
     char usock_file[MAXPATHLEN + 1];    
     int fd_table_size;
     int idle_timeout;
     int max_vols;
 };
 
-extern struct db_param *      db_param_read  (char *, enum identity);
-
+extern struct db_param *db_param_read  (char *);
 
 #endif /* CNID_DBD_DB_PARAM_H */
 
index 0dbff51dba97a4ac133c6ab20469470031c54987..b4f7e5d4a0013adfa8f50be1825d0e9ed7f664bc 100644 (file)
@@ -1,8 +1,6 @@
 /*
- * $Id: dbd.h,v 1.7 2009-11-25 14:59:15 franklahm Exp $
- *
  * Copyright (C) Joerg Lenneis 2003
- * Copyright (C) Frank Lahm 2009
+ * Copyright (C) Frank Lahm 2009, 2010
  * All Rights Reserved.  See COPYING.
  */
 
@@ -14,7 +12,6 @@
 extern int add_cnid(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply);
 extern int get_cnid(DBD *dbd, struct cnid_dbd_rply *rply);
 
-extern int dbd_stamp(DBD *dbd);
 extern int dbd_add(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *, int nolookup);
 extern int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *, int roflag);
 extern int dbd_get(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *);
@@ -23,6 +20,7 @@ extern int dbd_update(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *);
 extern int dbd_delete(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *, int idx);
 extern int dbd_getstamp(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *);
 extern int dbd_rebuild_add(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *);
+extern int dbd_search(DBD *dbd, struct cnid_dbd_rqst *, struct cnid_dbd_rply *);
 extern int dbd_check_indexes(DBD *dbd, char *);
 
 #endif /* CNID_DBD_DBD_H */
index 85676b2e575f5f63722d53047c6d20cd82ff6023..6a818a3b325063f27fe37a0a412d6081bf6bb0b8 100644 (file)
@@ -1,7 +1,6 @@
 /*
- * $Id: dbd_add.c,v 1.8 2010-01-19 14:57:11 franklahm Exp $
- *
  * Copyright (C) Joerg Lenneis 2003
+ * Copyright (C) Frank Lahm 2010
  * All Rights Reserved.  See COPYING.
  */
 
@@ -83,21 +82,35 @@ int add_cnid(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
 /* ---------------------- */
 int get_cnid(DBD *dbd, struct cnid_dbd_rply *rply)
 {
+    static cnid_t id;
+    static char buf[ROOTINFO_DATALEN];
     DBT rootinfo_key, rootinfo_data;
     int rc;
-    cnid_t hint, id;
-     
+    cnid_t hint;
+
     memset(&rootinfo_key, 0, sizeof(rootinfo_key));
     memset(&rootinfo_data, 0, sizeof(rootinfo_data));
     rootinfo_key.data = ROOTINFO_KEY;
     rootinfo_key.size = ROOTINFO_KEYLEN;
 
-    if ((rc = dbif_get(dbd, DBIF_CNID, &rootinfo_key, &rootinfo_data, 0)) <= 0) {
-        rply->result = CNID_DBD_RES_ERR_DB;
-        return -1;
+    if (id == 0) {
+        if ((rc = dbif_get(dbd, DBIF_CNID, &rootinfo_key, &rootinfo_data, 0)) < 0) {
+            rply->result = CNID_DBD_RES_ERR_DB;
+            return -1;
+        }
+        if (rc == 0) {
+            /* no rootinfo key yet */
+            memcpy(buf, ROOTINFO_DATA, ROOTINFO_DATALEN);
+            id = CNID_START - 1;
+        } else {
+            memcpy(buf, (char *)rootinfo_data.data, ROOTINFO_DATALEN);
+            memcpy(&hint, buf + CNID_TYPE_OFS, sizeof(hint));
+            id = ntohl(hint);
+            if (id < CNID_START - 1)
+                id = CNID_START - 1;
+        }
     }
-    memcpy(&hint, (char *)rootinfo_data.data +CNID_TYPE_OFS, sizeof(hint));
-    id = ntohl(hint);
+
     /* If we've hit the max CNID allowed, we return an error. CNID
      * needs to be recycled before proceding. */
     if (++id == CNID_INVALID) {
@@ -105,12 +118,10 @@ int get_cnid(DBD *dbd, struct cnid_dbd_rply *rply)
         return -1;
     }
 
-#if 0
-    memcpy(buf, ROOTINFO_DATA, ROOTINFO_DATALEN);
-#endif    
+    rootinfo_data.data = buf;
     rootinfo_data.size = ROOTINFO_DATALEN;
     hint = htonl(id);
-    memcpy((char *)rootinfo_data.data +CNID_TYPE_OFS, &hint, sizeof(hint));
+    memcpy(buf + CNID_TYPE_OFS, &hint, sizeof(hint));
 
     if (dbif_put(dbd, DBIF_CNID, &rootinfo_key, &rootinfo_data, 0) < 0) {
         rply->result = CNID_DBD_RES_ERR_DB;
@@ -120,44 +131,6 @@ int get_cnid(DBD *dbd, struct cnid_dbd_rply *rply)
     return 0;
 }
 
-/* --------------- 
-*/
-int dbd_stamp(DBD *dbd)
-{
-    DBT rootinfo_key, rootinfo_data;
-    cnid_t hint;
-    char buf[ROOTINFO_DATALEN];
-    char stamp[CNID_DEV_LEN];
-
-    memset(&rootinfo_key, 0, sizeof(rootinfo_key));
-    memset(&rootinfo_data, 0, sizeof(rootinfo_data));
-    rootinfo_key.data = ROOTINFO_KEY;
-    rootinfo_key.size = ROOTINFO_KEYLEN;
-
-    switch (dbif_get(dbd, DBIF_CNID, &rootinfo_key, &rootinfo_data, 0)) {
-    case 0:
-        hint = htonl(CNID_START);
-        memcpy(buf, ROOTINFO_DATA, ROOTINFO_DATALEN);
-        rootinfo_data.data = buf;
-        rootinfo_data.size = ROOTINFO_DATALEN;
-        if (dbif_stamp(dbd, stamp, CNID_DEV_LEN) < 0) {
-            return -1;
-        }
-        memcpy((char *)rootinfo_data.data + CNID_TYPE_OFS, &hint, sizeof(hint));
-        memcpy((char *)rootinfo_data.data + CNID_DEV_OFS, stamp, sizeof(stamp));
-        if (dbif_put(dbd, DBIF_CNID, &rootinfo_key, &rootinfo_data, 0) < 0) {
-            return -1;
-        }
-        return 0;
-    
-    case 1: /* we already have one */
-        return 0;
-    default:
-        return -1;
-    }
-    return -1;
-}
-
 /* ------------------------ */
 /* We need a nolookup version for `dbd` */
 int dbd_add(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply, int nolookup)
index b7fc936aa893c2df3cfaa4b4cc5020882cf0f01a..ea75c4aba67799356636162a70c5c0a4194c63a4 100644 (file)
@@ -1,3 +1,4 @@
+
 /*
  * $Id: dbd_getstamp.c,v 1.4 2009-05-06 11:54:24 franklahm Exp $
  *
index 6008d152dc10e1d6f550fe915ade078f7094c0cb..17a42de0ff1a5f362885edd4826fd9bbbc6e0521 100644 (file)
@@ -50,8 +50,8 @@ int dbd_resolve(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply
 
     memcpy(&rply->did, (char *) data.data + CNID_DID_OFS, sizeof(cnid_t));
 
-    rply->namelen = data.size - CNID_NAME_OFS;
-    rply->name = (char *)data.data + CNID_NAME_OFS;
+    rply->namelen = data.size;
+    rply->name = (char *)data.data;
 
     LOG(log_debug, logtype_cnid, "dbd_resolve: Resolving CNID %u to did %u name %s",
         ntohl(rqst->cnid), ntohl(rply->did), rply->name);
diff --git a/etc/cnid_dbd/dbd_search.c b/etc/cnid_dbd/dbd_search.c
new file mode 100644 (file)
index 0000000..a2efc33
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) Frank Lahm 2010
+ * All Rights Reserved.  See COPYING.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <errno.h>
+#include <netatalk/endian.h>
+#include <atalk/logger.h>
+#include <atalk/cnid_dbd_private.h>
+
+#include "dbif.h"
+#include "dbd.h"
+#include "pack.h"
+
+int dbd_search(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
+{
+    DBT key;
+    int results;
+    static char resbuf[DBD_MAX_SRCH_RSLTS * sizeof(cnid_t)];
+
+    LOG(log_debug, logtype_cnid, "dbd_search(\"%s\"):", rqst->name);
+
+    memset(&key, 0, sizeof(key));
+    rply->name = resbuf;
+    rply->namelen = 0;
+
+    key.data = rqst->name;
+    key.size = rqst->namelen;
+
+    if ((results = dbif_search(dbd, &key, resbuf)) < 0) {
+        LOG(log_error, logtype_cnid, "dbd_search(\"%s\"): db error", rqst->name);
+        rply->result = CNID_DBD_RES_ERR_DB;
+        return -1;
+    }
+    if (results) {
+        LOG(log_debug, logtype_cnid, "dbd_search(\"%s\"): %d matches", rqst->name, results);
+        rply->namelen = results * sizeof(cnid_t);
+        rply->result = CNID_DBD_RES_OK;
+    } else {
+        LOG(log_debug, logtype_cnid, "dbd_search(\"%s\"): no matches", rqst->name);
+        rply->result = CNID_DBD_RES_NOTFOUND;
+    }
+
+    return 1;
+}
index 504b230aac3575d73d6e03d514124c029bf051e7..d2ce83eca388ae6381be8ed2fa030a43dae58f0b 100644 (file)
@@ -1,7 +1,6 @@
 /*
- * $Id: dbd_update.c,v 1.7 2009-07-12 09:21:34 franklahm Exp $
- *
  * Copyright (C) Joerg Lenneis 2003
+ * Copyright (C) Frank Lahm 2009,2010
  * All Rights Reserved.  See COPYING.
  */
 
@@ -52,7 +51,7 @@ int dbd_update(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
     if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) < 0)
         goto err_db;
 
-    LOG(log_info, logtype_cnid, "dbd_update: Updated dbd with dev/ino: 0x%llx/0x%llx, did: %u, name: %s, cnid: %u",
+    LOG(log_debug, logtype_cnid, "dbd_update: Updated dbd with dev/ino: 0x%llx/0x%llx, did: %u, name: %s, cnid: %u",
         (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rqst->did), rqst->name, ntohl(rqst->cnid));
 
     rply->result = CNID_DBD_RES_OK;
index c40c8e8c29a007356684b86baf06e6b3a02dee81..e8cfdd4cfc51dfa21db6c75161429938b5f134d8 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: dbif.c,v 1.20 2010-01-19 14:57:11 franklahm Exp $
- *
  * Copyright (C) Joerg Lenneis 2003
  * Copyright (C) Frank Lahm 2009
  * All Rights Reserved.  See COPYING.
 #include <stdio.h>
 #include <errno.h>
 #include <stdlib.h>
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif /* HAVE_SYS_TYPES_H */
 #include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/cdefs.h>
 #include <unistd.h>
-#include <atalk/logger.h>
+
 #include <db.h>
+
+#include <atalk/logger.h>
+#include <atalk/util.h>
+
 #include "db_param.h"
 #include "dbif.h"
 #include "pack.h"
 
 #define DB_ERRLOGFILE "db_errlog"
 
-static char *old_dbfiles[] = {"cnid.db", NULL};
-
-/* --------------- */
-static int upgrade_required(const DBD *dbd)
+/*!
+ * Get the db stamp which is the st_ctime of the file "cnid2.db" and store it in buffer
+ */
+static int dbif_stamp(DBD *dbd, void *buffer, int size)
 {
-    int i;
-    int cwd = -1;
-    int ret = 0;
-    int found = 0;
     struct stat st;
+    int         rc,cwd;
 
-    if ( ! dbd->db_filename)
-        /* in memory db */
-        return 0;
+    if (size < 8)
+        return -1;
 
     /* Remember cwd */
     if ((cwd = open(".", O_RDONLY)) < 0) {
@@ -53,30 +48,182 @@ static int upgrade_required(const DBD *dbd)
     /* chdir to db_envhome */
     if ((chdir(dbd->db_envhome)) != 0) {
         LOG(log_error, logtype_cnid, "error chdiring to db_env '%s': %s", dbd->db_envhome, strerror(errno));        
+        return -1;
+    }
+
+    if ((rc = stat(dbd->db_table[DBIF_CNID].name, &st)) < 0) {
+        LOG(log_error, logtype_cnid, "error stating database %s: %s", dbd->db_table[DBIF_CNID].name, db_strerror(errno));
+        return -1;
+    }
+
+    LOG(log_maxdebug, logtype_cnid,"stamp: %s", asctime(localtime(&st.st_ctime)));
+
+    memset(buffer, 0, size);
+    memcpy(buffer, &st.st_ctime, sizeof(st.st_ctime));
+
+    if ((fchdir(cwd)) != 0) {
+        LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno));        
+        return -1;
+    }
+
+    return 0;
+}
+
+/*!
+ * Inititialize rootinfo key (which has CNID 0 as key)
+ *
+ * This also "stamps" the database, which means storing st.st_ctime of the
+ * "cnid2.db" file in the rootinfo data at the DEV offset
+ *
+ * @param dbd      (rw) database handle
+ * @param version  (r)  database version number
+ *
+ * @returns -1 on error, 0 on success
+ */
+static int dbif_init_rootinfo(DBD *dbd, int version)
+{
+    DBT key, data;
+    uint32_t v;
+    char buf[ROOTINFO_DATALEN];
+
+    LOG(log_debug, logtype_cnid, "Setting CNID database version to %u", version);
+
+    v = version;
+    v = htonl(v);
+
+    memset(&key, 0, sizeof(key));
+    memset(&data, 0, sizeof(data));
+    key.data = ROOTINFO_KEY;
+    key.size = ROOTINFO_KEYLEN;
+    data.data = buf;
+    data.size = ROOTINFO_DATALEN;
+
+    memcpy(buf, ROOTINFO_DATA, ROOTINFO_DATALEN);
+    memcpy(buf + CNID_DID_OFS, &v, sizeof(v));
+    if (dbif_stamp(dbd, buf + CNID_DEV_OFS, CNID_DEV_LEN) < 0)
+        return -1;
+
+    if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) < 0)
+        return -1;
+    if (dbif_txn_commit(dbd) != 1) {
+        LOG(log_error, logtype_cnid, "dbif_init_rootinfo: cant commit txn");
+        return -1;
+    }
+
+    return 0;
+}
+
+/*!
+ * Return CNID database version number
+ *
+ * Returns version in *version
+ *
+ * @returns -1 on error, 0 if theres no rootinfo key yet, 1 if *version is returned
+ */
+static int dbif_getversion(DBD *dbd, uint32_t *version)
+{
+    DBT key, data;
+    int ret;
+
+    LOG(log_maxdebug, logtype_cnid, "dbif_getversion: reading version info");
+
+    *version = -1;
+    memset(&key, 0, sizeof(key));
+    memset(&data, 0, sizeof(data));
+    key.data = ROOTINFO_KEY;
+    key.size = ROOTINFO_KEYLEN;
+
+    switch (dbif_get(dbd, DBIF_CNID, &key, &data, 0)) {
+    case 1: /* found */
+        memcpy(version, (char *)data.data + CNID_DID_OFS, sizeof(uint32_t));
+        *version = ntohl(*version);
+        LOG(log_debug, logtype_cnid, "CNID database version %u", *version);
+        ret = 1;
+        break;
+    case 0: /* not found */
+        LOG(log_debug, logtype_cnid, "dbif_getversion: no version info found");
+        ret = 0;
+        break;
+    default:
+        LOG(log_error, logtype_cnid, "dbif_getversion: database error");
         ret = -1;
-        goto exit;
+        break;
     }
 
-    for (i = 0; old_dbfiles[i] != NULL; i++) {
-        if ( !(stat(old_dbfiles[i], &st) < 0) ) {
-            found++;
-            continue;
-        }
-        if (errno != ENOENT) {
-            LOG(log_error, logtype_cnid, "cnid_open: Checking %s gave %s", old_dbfiles[i], strerror(errno));
-            found++;
-        }
+    return ret;
+}
+
+/*!
+ * Set CNID database version number
+ *
+ * Initializes rootinfo key as neccessary
+ * @returns -1 on error, 0 on success
+ */
+static int dbif_setversion(DBD *dbd, uint32_t version)
+{
+    int ret;
+    DBT key, data;
+    uint32_t v;
+
+    LOG(log_debug, logtype_cnid, "Setting CNID database version to %u", version);
+
+    v = version;
+    v = htonl(v);
+
+    memset(&key, 0, sizeof(key));
+    memset(&data, 0, sizeof(data));
+    key.data = ROOTINFO_KEY;
+    key.size = ROOTINFO_KEYLEN;
+
+    if ((ret = dbif_get(dbd, DBIF_CNID, &key, &data, 0)) == -1)
+        return -1;
+    if (ret == 0) {
+        /* No rootinfo key yet, init it */
+        if (dbif_init_rootinfo(dbd, CNID_VERSION) != 0)
+            return -1;
+        /* Now try again */
+        if (dbif_get(dbd, DBIF_CNID, &key, &data, 0) == -1)
+            return -1;
     }
+    memcpy((char *)data.data + CNID_DID_OFS, &v, sizeof(v));
+    data.size = ROOTINFO_DATALEN;
+    if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) < 0)
+        return -1;
 
-exit:
-    if (cwd != -1) {
-        if ((fchdir(cwd)) != 0) {
-            LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno));        
-            ret = -1;
-        }
-        close(cwd);
+    return 0;
+}
+
+/*!
+ * Upgrade CNID database versions, initialize rootinfo key as as necessary in dbif_setversion()
+ *
+ * For now this does nothing, as upgrading from ver. 0 to 1 is done in dbif_open
+ */
+#define UNINTIALIZED_DB UINT32_MAX
+static int dbif_upgrade(DBD *dbd)
+{
+    uint32_t version = CNID_VERSION_UNINTIALIZED_DB;
+
+    if (dbif_getversion(dbd, &version) == -1)
+        return -1;
+    if (version == CNID_VERSION_UNINTIALIZED_DB) {
+        version = CNID_VERSION;
+        if (dbif_setversion(dbd, CNID_VERSION) != 0)
+            return -1;
+    }
+
+    /* 
+     * Do upgrade stuff ...
+     */
+
+    /* Write current version to database */
+    if (version != CNID_VERSION) {
+        if (dbif_setversion(dbd, CNID_VERSION) != 0)
+            return -1;
     }
-    return (ret < 0 ? ret : found);
+
+    LOG(log_debug, logtype_cnid, "Finished CNID database version upgrade check");
+
+    return 0;
 }
 
 /* --------------- */
@@ -174,42 +321,6 @@ exit:
     return ret;
 }
 
-/* --------------- */
-int dbif_stamp(DBD *dbd, void *buffer, int size)
-{
-    struct stat st;
-    int         rc,cwd;
-
-    if (size < 8)
-        return -1;
-
-    /* Remember cwd */
-    if ((cwd = open(".", O_RDONLY)) < 0) {
-        LOG(log_error, logtype_cnid, "error opening cwd: %s", strerror(errno));
-        return -1;
-    }
-
-    /* chdir to db_envhome */
-    if ((chdir(dbd->db_envhome)) != 0) {
-        LOG(log_error, logtype_cnid, "error chdiring to db_env '%s': %s", dbd->db_envhome, strerror(errno));        
-        return -1;
-    }
-
-    if ((rc = stat(dbd->db_table[DBIF_CNID].name, &st)) < 0) {
-        LOG(log_error, logtype_cnid, "error stating database %s: %s", dbd->db_table[DBIF_CNID].name, db_strerror(errno));
-        return -1;
-    }
-    memset(buffer, 0, size);
-    memcpy(buffer, &st.st_ctime, sizeof(st.st_ctime));
-
-    if ((fchdir(cwd)) != 0) {
-        LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno));        
-        return -1;
-    }
-
-    return 0;
-}
-
 /* --------------- */
 DBD *dbif_init(const char *envhome, const char *filename)
 {
@@ -240,14 +351,19 @@ DBD *dbif_init(const char *envhome, const char *filename)
     dbd->db_table[DBIF_CNID].name        = "cnid2.db";
     dbd->db_table[DBIF_IDX_DEVINO].name  = "devino.db";
     dbd->db_table[DBIF_IDX_DIDNAME].name = "didname.db";
+    dbd->db_table[DBIF_IDX_NAME].name    = "name.db";
 
     dbd->db_table[DBIF_CNID].type        = DB_BTREE;
     dbd->db_table[DBIF_IDX_DEVINO].type  = DB_BTREE;
     dbd->db_table[DBIF_IDX_DIDNAME].type = DB_BTREE;
+    dbd->db_table[DBIF_IDX_NAME].type    = DB_BTREE;
 
     dbd->db_table[DBIF_CNID].openflags        = DB_CREATE;
     dbd->db_table[DBIF_IDX_DEVINO].openflags  = DB_CREATE;
     dbd->db_table[DBIF_IDX_DIDNAME].openflags = DB_CREATE;
+    dbd->db_table[DBIF_IDX_NAME].openflags    = DB_CREATE;
+
+    dbd->db_table[DBIF_IDX_NAME].flags = DB_DUPSORT;
 
     return dbd;
 }
@@ -255,7 +371,7 @@ DBD *dbif_init(const char *envhome, const char *filename)
 /* 
    We must open the db_env with an absolute pathname, as `dbd` keeps chdir'ing, which
    breaks e.g. bdb logfile-rotation with relative pathnames.
-   But still we use relative paths with upgrade_required() and the DB_ERRLOGFILE
+   But still we use relative paths with DB_ERRLOGFILE
    in order to avoid creating absolute paths by copying. Both have no problem with
    a relative path.
 */
@@ -263,12 +379,6 @@ int dbif_env_open(DBD *dbd, struct db_param *dbp, uint32_t dbenv_oflags)
 {
     int ret;
 
-    /* Refuse to do anything if this is an old version of the CNID database */
-    if (upgrade_required(dbd)) {
-        LOG(log_error, logtype_cnid, "Found version 1 of the CNID database. Please upgrade to version 2");
-        return -1;
-    }
-
     if ((ret = db_env_create(&dbd->db_env, 0))) {
         LOG(log_error, logtype_cnid, "error creating DB environment: %s",
             db_strerror(ret));
@@ -276,6 +386,8 @@ int dbif_env_open(DBD *dbd, struct db_param *dbp, uint32_t dbenv_oflags)
         return -1;
     }
 
+    dbd->db_param = *dbp;
+
     if ((dbif_openlog(dbd)) != 0)
         return -1;
 
@@ -326,6 +438,22 @@ int dbif_env_open(DBD *dbd, struct db_param *dbp, uint32_t dbenv_oflags)
         return -1;
     }
 
+    if ((ret = dbd->db_env->set_lk_max_locks(dbd->db_env, dbp->maxlocks))) {
+        LOG(log_error, logtype_cnid, "error setting DB environment maxlocks to %i: %s",
+            10000, db_strerror(ret));
+        dbd->db_env->close(dbd->db_env, 0);
+        dbd->db_env = NULL;
+        return -1;
+    }
+
+    if ((ret = dbd->db_env->set_lk_max_objects(dbd->db_env, dbp->maxlockobjs))) {
+        LOG(log_error, logtype_cnid, "error setting DB environment max lockobjects to %i: %s",
+            10000, db_strerror(ret));
+        dbd->db_env->close(dbd->db_env, 0);
+        dbd->db_env = NULL;
+        return -1;
+    }
+
     if ((ret = dbd->db_env->open(dbd->db_env, dbd->db_envhome, dbenv_oflags, 0))) {
         LOG(log_error, logtype_cnid, "error opening DB environment after recovery: %s",
             db_strerror(ret));
@@ -408,7 +536,7 @@ int dbif_open(DBD *dbd, struct db_param *dbp, int reindex)
                 LOG(log_error, logtype_cnid, "error forcing checkpoint: %s", db_strerror(ret));
                 return -1;
             }
-            LOG(log_debug, logtype_cnid, "Finished CNID database upgrade check");
+            LOG(log_debug, logtype_cnid, "Finished BerkeleyBD upgrade check");
         }
         
         if ((fchdir(cwd)) != 0) {
@@ -497,6 +625,38 @@ int dbif_open(DBD *dbd, struct db_param *dbp, int reindex)
     }
     if (reindex)
         LOG(log_info, logtype_cnid, "... done.");
+
+    if (reindex)
+        LOG(log_info, logtype_cnid, "Reindexing name index...");
+
+    /*
+     * Upgrading from version 0 to 1 requires adding the name index below which
+     * must be done by specifying the DB_CREATE flag
+     */
+    uint32_t version = CNID_VERSION;
+    if (dbd->db_envhome && !reindex) {
+        if (dbif_getversion(dbd, &version) == -1)
+            return -1;
+    }
+
+    if ((ret = dbd->db_table[0].db->associate(dbd->db_table[0].db, 
+                                              dbd->db_txn,
+                                              dbd->db_table[DBIF_IDX_NAME].db, 
+                                              idxname,
+                                              (reindex
+                                               || 
+                                               ((CNID_VERSION == CNID_VERSION_1) && (version == CNID_VERSION_0)))
+                                              ? DB_CREATE : 0)) != 0) {
+        LOG(log_error, logtype_cnid, "Failed to associate name index: %s", db_strerror(ret));
+        return -1;
+    }
+    if (reindex)
+        LOG(log_info, logtype_cnid, "... done.");
+
+    if ((dbd->db_envhome) && ((ret = dbif_upgrade(dbd)) != 0)) {
+        LOG(log_error, logtype_cnid, "Error upgrading CNID database to version %d", CNID_VERSION);
+        return -1;
+    }
     
     return 0;
 }
@@ -550,7 +710,7 @@ int dbif_close(DBD *dbd)
    In order to support silent database upgrades:
    destroy env at cnid_dbd shutdown.
  */
-int dbif_prep_upgrade(const char *path)
+int dbif_env_remove(const char *path)
 {
     int ret;
     DBD *dbd;
@@ -717,8 +877,10 @@ int dbif_del(DBD *dbd, const int dbi, DBT *key, u_int32_t flags)
                                      key,
                                      flags);
     
-    if (ret == DB_NOTFOUND || ret == DB_SECONDARY_BAD)
+    if (ret == DB_NOTFOUND) {
+        LOG(log_info, logtype_cnid, "key not found");
         return 0;
+    }
     if (ret) {
         LOG(log_error, logtype_cnid, "error deleting key/value from %s: %s",
             dbd->db_table[dbi].name, db_strerror(ret));
@@ -727,6 +889,60 @@ int dbif_del(DBD *dbd, const int dbi, DBT *key, u_int32_t flags)
         return 1;
 }
 
+/*!
+ * Search the database by name
+ *
+ * @param resbuf    (w) buffer for search results CNIDs, maxsize is assumed to be
+ *                      DBD_MAX_SRCH_RSLTS * sizefof(cnid_t)
+ *
+ * @returns -1 on error, 0 when nothing found, else the number of matches
+ */
+int dbif_search(DBD *dbd, DBT *key, char *resbuf)
+{
+    int ret = 0;
+    int count = 0;
+    DBC *cursorp = NULL;
+    DBT pkey, data;
+    char *cnids = resbuf;
+    cnid_t cnid;
+    char *namebkp = key->data;
+    int namelenbkp = key->size;
+
+    memset(&pkey, 0, sizeof(DBT));
+    memset(&data, 0, sizeof(DBT));
+
+    /* Get a cursor */
+    ret = dbd->db_table[DBIF_IDX_NAME].db->cursor(dbd->db_table[DBIF_IDX_NAME].db,
+                                                  NULL,
+                                                  &cursorp,
+                                                  0);
+    if (ret != 0) {
+        LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(ret));
+        ret = -1;
+        goto exit;
+    }
+
+    ret = cursorp->pget(cursorp, key, &pkey, &data, DB_SET_RANGE);
+    while (count < DBD_MAX_SRCH_RSLTS && ret != DB_NOTFOUND) {
+        if (!((namelenbkp <= key->size) && (strncmp(namebkp, key->data, namelenbkp) == 0)))
+            break;
+        count++;
+        memcpy(cnids, pkey.data, sizeof(cnid_t));
+        memcpy(&cnid, pkey.data, sizeof(cnid_t));
+        cnids += sizeof(cnid_t);
+        LOG(log_debug, logtype_cnid, "match: CNID %" PRIu32, ntohl(cnid));
+
+        ret = cursorp->pget(cursorp, key, &pkey, &data, DB_NEXT);
+    }
+
+    ret = count;
+
+exit:
+    if (cursorp != NULL)
+        cursorp->close(cursorp);
+    return ret;
+}
+
 int dbif_txn_begin(DBD *dbd)
 {
     int ret;
@@ -791,25 +1007,42 @@ int dbif_txn_abort(DBD *dbd)
 }
 
 /* 
-   ret = 1 -> commit txn
-   ret = 0 -> abort txn -> exit!
+   ret = 2 -> commit txn regardless of db_param.txn_frequency
+   ret = 1 -> commit txn if db_param.txn_frequency
+   ret = 0 -> abort txn db_param.txn_frequency -> exit!
    anything else -> exit!
+
+   db_param of the db environment might specify txn_frequency > 1 in which case
+   we only close a txn every txn_frequency time. the `dbd` command uses this for the
+   temp rebuild db, cnid_dbd keeps it at 0. For increasing cnid_dbd throughput this
+   should be tuned and testes as well.
+
+   @returns 0 on success (abort or commit), -1 on error
 */
-void dbif_txn_close(DBD *dbd, int ret)
+int dbif_txn_close(DBD *dbd, int ret)
 {
     if (ret == 0) {
         if (dbif_txn_abort(dbd) < 0) {
             LOG( log_error, logtype_cnid, "Fatal error aborting transaction. Exiting!");
-            exit(EXIT_FAILURE);
+            return -1;
+        }
+    } else if (ret == 1 || ret == 2) {
+        static uint count;
+        if (ret != 2 && dbd->db_param.txn_frequency > 1) {
+            count++;
+            if ((count % dbd->db_param.txn_frequency) != 0)
+                return 0;
         }
-    } else if (ret == 1) {
         ret = dbif_txn_commit(dbd);
         if (  ret < 0) {
             LOG( log_error, logtype_cnid, "Fatal error committing transaction. Exiting!");
-            exit(EXIT_FAILURE);
+            return -1;
         }
-    } else
-       exit(EXIT_FAILURE);
+    } else {
+        return -1;
+    }
+
+    return 0;
 }
 
 int dbif_txn_checkpoint(DBD *dbd, u_int32_t kbyte, u_int32_t min, u_int32_t flags)
@@ -873,7 +1106,7 @@ int dbif_copy_rootinfokey(DBD *srcdbd, DBD *destdbd)
 int dbif_dump(DBD *dbd, int dumpindexes)
 {
     int rc;
-    uint32_t max = 0, count = 0, cnid, type, did, lastid;
+    uint32_t max = 0, count = 0, cnid, type, did, lastid, version;
     uint64_t dev, ino;
     time_t stamp;
     DBC *cur;
@@ -900,11 +1133,15 @@ int dbif_dump(DBD *dbd, int dumpindexes)
 
         /* Rootinfo node ? */
         if (cnid == 0) {
-            memcpy(&stamp, (char *)data.data + 4, sizeof(time_t));
-            memcpy(&lastid, (char *)data.data + 20, sizeof(cnid_t));
+            memcpy(&stamp, (char *)data.data + CNID_DEV_OFS, sizeof(time_t));
+            memcpy(&lastid, (char *)data.data + CNID_TYPE_OFS, CNID_TYPE_LEN);
             lastid = ntohl(lastid);
+            memcpy(&version, (char *)data.data + CNID_DID_OFS, CNID_DID_LEN);
+            version = ntohl(version);
+
             strftime(timebuf, sizeof(timebuf), "%b %d %Y %H:%M:%S", localtime(&stamp));
-            printf("dbd stamp: 0x%08x (%s), next free CNID: %u\n", (unsigned int)stamp, timebuf, lastid + 1);
+            printf("CNID db version: %u, dbd stamp: 0x%08x (%s), next free CNID: %u\n",
+                   version, (unsigned int)stamp, timebuf, lastid + 1);
         } else {
             /* dev */
             memcpy(&dev, (char *)data.data + CNID_DEV_OFS, 8);
@@ -1049,9 +1286,11 @@ int dbif_idwalk(DBD *dbd, cnid_t *cnid, int close)
     static DBT key = { 0 }, data = { 0 };
     DB *db = dbd->db_table[DBIF_CNID].db;
 
-    if (close && dbd->db_cur) {
-        dbd->db_cur->close(dbd->db_cur);
-        dbd->db_cur = NULL;
+    if (close) {
+        if (dbd->db_cur) {
+            dbd->db_cur->close(dbd->db_cur);
+            dbd->db_cur = NULL;
+        }
         return 0;
     }
 
index c63f302170bcfd0438eec7e6f425edb2bc752e88..f5d3759fca5affba4013d724e2b32813b70309dd 100644 (file)
@@ -1,6 +1,4 @@
 /*
-  $Id: dbif.h,v 1.9 2009-12-21 06:41:09 franklahm Exp $
   Copyright (C) Joerg Lenneis 2003
   Copyright (C) Frank Lahm 2009
   All Rights Reserved.  See COPYING.
 #include <atalk/adouble.h>
 #include "db_param.h"
 
-#define DBIF_DB_CNT 3
+#define DBIF_DB_CNT 4
  
 #define DBIF_CNID          0
 #define DBIF_IDX_DEVINO    1
 #define DBIF_IDX_DIDNAME   2
+#define DBIF_IDX_NAME      3
 
 /* Structures */
 typedef struct {
@@ -77,12 +76,13 @@ typedef struct {
 
 typedef struct {
     DB_ENV   *db_env;
+    struct db_param db_param;
     DB_TXN   *db_txn;
     DBC      *db_cur;              /* for dbif_walk */
     char     *db_envhome;
     char     *db_filename;
     FILE     *db_errlog;
-    db_table db_table[3];
+    db_table db_table[DBIF_DB_CNT];
 } DBD;
 
 /* Functions */
@@ -90,19 +90,19 @@ extern DBD *dbif_init(const char *envhome, const char *dbname);
 extern int dbif_env_open(DBD *dbd, struct db_param *dbp, uint32_t dbenv_oflags);
 extern int dbif_open(DBD *dbd, struct db_param *dbp, int reindex);
 extern int dbif_close(DBD *dbd);
-extern int dbif_prep_upgrade(const char *path);
+extern int dbif_env_remove(const char *path);
 
 extern int dbif_get(DBD *, const int, DBT *, DBT *, u_int32_t);
 extern int dbif_pget(DBD *, const int, DBT *, DBT *, DBT *, u_int32_t);
 extern int dbif_put(DBD *, const int, DBT *, DBT *, u_int32_t);
 extern int dbif_del(DBD *, const int, DBT *, u_int32_t);
 extern int dbif_count(DBD *, const int, u_int32_t *);
-extern int dbif_stamp(DBD *, void *, int);
+extern int dbif_search(DBD *dbd, DBT *key, char *resbuf);
 extern int dbif_copy_rootinfokey(DBD *srcdbd, DBD *destdbd);
 extern int dbif_txn_begin(DBD *);
 extern int dbif_txn_commit(DBD *);
 extern int dbif_txn_abort(DBD *);
-extern void dbif_txn_close(DBD *dbd, int ret); /* Switch between commit+abort */
+extern int dbif_txn_close(DBD *dbd, int ret); /* Switch between commit+abort */
 extern int dbif_txn_checkpoint(DBD *, u_int32_t, u_int32_t, u_int32_t);
 
 extern int dbif_dump(DBD *dbd, int dumpindexes);
index 42cf28489f14e61c055a73a73eb9a75a74742347..da4cd9a4e7085d764590140786580a3e82a3bb9d 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: main.c,v 1.16 2009-11-25 14:59:15 franklahm Exp $
- *
  * Copyright (C) Joerg Lenneis 2003
  * Copyright (c) Frank Lahm 2009
  * All Rights Reserved.  See COPYING.
@@ -34,6 +32,7 @@
 #include <netatalk/endian.h>
 #include <atalk/cnid_dbd_private.h>
 #include <atalk/logger.h>
+#include <atalk/volinfo.h>
 
 #include "db_param.h"
 #include "dbif.h"
  */
 #define DBOPTIONS (DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN | DB_RECOVER)
 
-static DBD *dbd;
+/* Global, needed by pack.c:idxname() */
+struct volinfo volinfo;
 
+static DBD *dbd;
 static int exit_sig = 0;
 
 static void sig_exit(int signo)
@@ -176,12 +177,15 @@ static int loop(struct db_param *dbp)
             case CNID_DBD_OP_REBUILD_ADD:
                 ret = dbd_rebuild_add(dbd, &rqst, &rply);
                 break;
+            case CNID_DBD_OP_SEARCH:
+                ret = dbd_search(dbd, &rqst, &rply);
+                break;
             default:
                 LOG(log_error, logtype_cnid, "loop: unknown op %d", rqst.op);
                 ret = -1;
                 break;
             }
-            
+
             if ((cret = comm_snd(&rply)) < 0 || ret < 0) {
                 dbif_txn_abort(dbd);
                 return -1;
@@ -270,6 +274,7 @@ static int get_lock(void)
 
     if (fcntl(lockfd, F_SETLK, &lock) < 0) {
         if (errno == EACCES || errno == EAGAIN) {
+            LOG(log_error, logtype_cnid, "get_lock: locked");
             exit(0);
         } else {
             LOG(log_error, logtype_cnid, "main: fcntl F_WRLCK lockfile: %s", strerror(errno));
@@ -321,7 +326,7 @@ int main(int argc, char *argv[])
     struct db_param *dbp;
     int err = 0;
     int lockfd, ctrlfd, clntfd;
-    char *dir, *logconfig;
+    char *logconfig;
 
     set_processname("cnid_dbd");
 
@@ -331,30 +336,47 @@ int main(int argc, char *argv[])
         exit(1);
     }
 
-    dir = argv[1];
     ctrlfd = atoi(argv[2]);
     clntfd = atoi(argv[3]);
     logconfig = strdup(argv[4]);
     setuplog(logconfig);
 
-    switch_to_user(dir);
+    /* Load .volinfo file */
+    if (loadvolinfo(argv[1], &volinfo) == -1) {
+        LOG(log_error, logtype_cnid, "Cant load volinfo for \"%s\"", argv[1]);
+        exit(EXIT_FAILURE);
+    }
+    /* Put "/.AppleDB" at end of volpath, get path from volinfo file */
+    char dbpath[MAXPATHLEN+1];
+    if ((strlen(volinfo.v_dbpath) + strlen("/.AppleDB")) > MAXPATHLEN ) {
+        LOG(log_error, logtype_cnid, "CNID db pathname too long: \"%s\"", volinfo.v_dbpath);
+        exit(EXIT_FAILURE);
+    }
+    strncpy(dbpath, volinfo.v_dbpath, MAXPATHLEN - strlen("/.AppleDB"));
+    strcat(dbpath, "/.AppleDB");
+
+    if (vol_load_charsets(&volinfo) == -1) {
+        LOG(log_error, logtype_cnid, "Error loading charsets!");
+        exit(EXIT_FAILURE);
+    }
+    LOG(log_debug, logtype_cnid, "db dir: \"%s\"", dbpath);
+
+    switch_to_user(dbpath);
 
     /* Before we do anything else, check if there is an instance of cnid_dbd
        running already and silently exit if yes. */
     lockfd = get_lock();
 
-    LOG(log_info, logtype_cnid, "Startup, DB dir %s", dir);
-
     set_signal();
 
     /* SIGINT and SIGTERM are always off, unless we are in pselect */
     block_sigs_onoff(1);
 
-    if ((dbp = db_param_read(dir, CNID_DBD)) == NULL)
+    if ((dbp = db_param_read(dbpath)) == NULL)
         exit(1);
     LOG(log_maxdebug, logtype_cnid, "Finished parsing db_param config file");
 
-    if (NULL == (dbd = dbif_init(".", "cnid2.db")))
+    if (NULL == (dbd = dbif_init(dbpath, "cnid2.db")))
         exit(2);
 
     if (dbif_env_open(dbd, dbp, DBOPTIONS) < 0)
@@ -367,12 +389,6 @@ int main(int argc, char *argv[])
     }
     LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
 
-    if (dbd_stamp(dbd) < 0) {
-        dbif_close(dbd);
-        exit(5);
-    }
-    LOG(log_maxdebug, logtype_cnid, "Finished checking database stamp");
-
     if (comm_init(dbp, ctrlfd, clntfd) < 0) {
         dbif_close(dbd);
         exit(3);
@@ -384,7 +400,7 @@ int main(int argc, char *argv[])
     if (dbif_close(dbd) < 0)
         err++;
 
-    if (dbif_prep_upgrade(dir) < 0)
+    if (dbif_env_remove(dbpath) < 0)
         err++;
 
     free_lock(lockfd);
index 1b4a15be588646fff8814ff9f2557abc08d02f7e..2612469415eaa5582d71712bca82dc5b63c33770 100644 (file)
@@ -1,7 +1,6 @@
 /*
- * $Id: pack.c,v 1.6 2009-10-13 22:55:37 didg Exp $
- *
  * Copyright (C) Joerg Lenneis 2003
+ * Copyright (C) Frank Lahm 2010
  * All Rights Reserved.  See COPYING.
  */
 
 #include <netatalk/endian.h>
 
 #include <string.h>
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif /* HAVE_SYS_TYPES_H */
+#include <inttypes.h>
 #include <sys/param.h>
 #include <sys/cdefs.h>
 #include <db.h>
 
+#include <atalk/unicode.h>
+#include <atalk/volinfo.h>
+#include <atalk/logger.h>
 #include <atalk/cnid_dbd_private.h>
 #include "pack.h"
 
+/* in main.c for `cnid_dbd` or cmd_dbd.c for `dbd` */
+extern struct volinfo volinfo;
+
 /* --------------- */
 /*
  *  This implementation is portable, but could probably be faster by using htonl
@@ -72,6 +75,29 @@ int devino(DB *dbp _U_, const DBT *pkey _U_,  const DBT *pdata, DBT *skey)
     return (0);
 }
 
+/* --------------- */
+int idxname(DB *dbp _U_, const DBT *pkey _U_,  const DBT *pdata, DBT *skey)
+{
+    static char buffer[MAXPATHLEN +2];
+    uint16_t flags = CONV_TOLOWER;
+    memset(skey, 0, sizeof(DBT));
+
+    if (convert_charset(volinfo.v_volcharset,
+                        volinfo.v_volcharset,
+                        volinfo.v_maccharset,
+                        (char *)pdata->data + CNID_NAME_OFS,
+                        strlen((char *)pdata->data + CNID_NAME_OFS),
+                        buffer,
+                        MAXPATHLEN,
+                        &flags) == (size_t)-1) {
+        LOG(log_error, logtype_cnid, "idxname: conversion error");
+    }
+
+    skey->data = buffer;
+    skey->size = strlen(skey->data);
+    return (0);
+}
+
 /* The equivalent to make_cnid_data in the cnid library. Non re-entrant. We
    differ from make_cnid_data in that we never return NULL, rqst->name cannot
    ever cause start[] to overflow because name length is checked in libatalk. */
index fb6984991d666e9caad2011c14abb63ad0674931..e94ef0d9a11a827faf2aa3b3e4145ce52df0b52b 100644 (file)
@@ -1,7 +1,6 @@
 /*
- * $Id: pack.h,v 1.5 2009-05-04 09:09:43 franklahm Exp $
- *
  * Copyright (C) Joerg Lenneis 2003
+ * Copyright (C) Frank Lahm 2010
  * All Rights Reserved.  See COPYING.
  */
 
@@ -23,5 +22,6 @@
 extern unsigned char *pack_cnid_data(struct cnid_dbd_rqst *);
 extern int didname(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey);
 extern int devino(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey);
+extern int idxname(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey);
 
 #endif /* CNID_DBD_PACK_H */
index 6c97fe73b56ea1925402720c9988523378b3fcf3..ceb6ff211ad6628693b18e48bb2c1fbec2925c03 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: usockfd.c,v 1.6 2009-11-05 14:38:07 franklahm Exp $
- *
  * Copyright (C) Joerg Lenneis 2003
  * All Rights Reserved.  See COPYING.
  */
@@ -67,6 +65,9 @@ int usockfd_create(char *usock_fn, mode_t mode, int backlog)
         return -1;
     }
 
+#ifdef chmod
+#undef chmod
+#endif
     if (chmod(usock_fn, mode) < 0) {
         LOG(log_error, logtype_cnid, "error changing permissions for %s: %s",
             usock_fn, strerror(errno));
@@ -154,7 +155,6 @@ int usockfd_check(int sockfd, const sigset_t *sigset)
     socklen_t size;
     fd_set readfds;
     int ret;
-    struct timeval tv;
      
     FD_ZERO(&readfds);
     FD_SET(sockfd, &readfds);
@@ -176,13 +176,6 @@ int usockfd_check(int sockfd, const sigset_t *sigset)
                 strerror(errno));
             return -1;
         }
-        tv.tv_sec = 5;
-        tv.tv_usec = 0;
-        if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
-            LOG(log_error, logtype_cnid, "set SO_RCVTIMEO: %s", strerror(errno));
-            close(fd);
-            return -1;
-        }
         return fd;
     } else
         return 0;
index fde2df74549344320ed84db0b5288cb6bd49ef5a..46c0d9700b7248fae5a424dd52d9109135dfdd29 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: uams_dhx2_pam.c,v 1.12 2010-03-30 10:25:49 franklahm Exp $
- *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
  * All Rights Reserved.  See COPYRIGHT.
 #define PRIMEBITS 1024
 
 /* hash a number to a 16-bit quantity */
-#define dhxhash(a) ((((unsigned long) (a) >> 8) ^ \
+#define dhxhash(a) ((((unsigned long) (a) >> 8) ^   \
                      (unsigned long) (a)) & 0xffff)
 
 /* Some parameters need be maintained across calls */
-static gcry_mpi_t p, Ra;
+static gcry_mpi_t p, g, Ra;
 static gcry_mpi_t serverNonce;
 static char *K_MD5hash = NULL;
 static int K_hash_len;
@@ -64,7 +62,7 @@ static struct passwd *dhxpwd;
 /*********************************************************
  * Crypto helper func to generate p and g for use in DH.
  * libgcrypt doesn't provide one directly.
- * Algorithm taken from GNUTLS:gnutls_dh_primes.c 
+ * Algorithm taken from GNUTLS:gnutls_dh_primes.c
  *********************************************************/
 
 /**
@@ -72,13 +70,9 @@ static struct passwd *dhxpwd;
  * the Diffie-Hellman key exchange.
  * The bits value should be one of 768, 1024, 2048, 3072 or 4096.
  **/
-
-static int
-dh_params_generate (gcry_mpi_t *ret_p, gcry_mpi_t *ret_g, unsigned int bits) {
+static int dh_params_generate (unsigned int bits) {
 
     int result, times = 0, qbits;
-
-    gcry_mpi_t g = NULL, prime = NULL;
     gcry_mpi_t *factors = NULL;
     gcry_error_t err;
 
@@ -91,59 +85,47 @@ dh_params_generate (gcry_mpi_t *ret_p, gcry_mpi_t *ret_g, unsigned int bits) {
     }
 
     if (bits < 256)
-       qbits = bits / 2;
+        qbits = bits / 2;
     else
-       qbits = (bits / 40) + 105;
+        qbits = (bits / 40) + 105;
 
     if (qbits & 1) /* better have an even number */
-       qbits++;
+        qbits++;
 
     /* find a prime number of size bits. */
     do {
-       if (times) {
-           gcry_mpi_release (prime);
-           gcry_prime_release_factors (factors);
-       }
-       err = gcry_prime_generate (&prime, bits, qbits, &factors, NULL, NULL,
-                                  GCRY_STRONG_RANDOM, GCRY_PRIME_FLAG_SPECIAL_FACTOR);
-       if (err != 0) {
-           result = AFPERR_MISC;
-           goto error;
-       }
-       err = gcry_prime_check (prime, 0);
-       times++;
+        if (times) {
+            gcry_mpi_release(p);
+            gcry_prime_release_factors (factors);
+        }
+        err = gcry_prime_generate(&p, bits, qbits, &factors, NULL, NULL,
+                                  GCRY_STRONG_RANDOM, GCRY_PRIME_FLAG_SPECIAL_FACTOR);
+        if (err != 0) {
+            result = AFPERR_MISC;
+            goto error;
+        }
+        err = gcry_prime_check(p, 0);
+        times++;
     } while (err != 0 && times < 10);
-    
+
     if (err != 0) {
-       result = AFPERR_MISC;
-       goto error;
+        result = AFPERR_MISC;
+        goto error;
     }
 
     /* generate the group generator. */
-    err = gcry_prime_group_generator (&g, prime, factors, NULL);
+    err = gcry_prime_group_generator(&g, p, factors, NULL);
     if (err != 0) {
-       result = AFPERR_MISC;
-       goto error;
-    }
-    
-    gcry_prime_release_factors (factors);
-    factors = NULL;
-    
-    if (ret_g)
-       *ret_g = g;
-    else
-       gcry_mpi_release (g);
-    if (ret_p)
-       *ret_p = prime;
-    else
-       gcry_mpi_release (prime);
-    
+        result = AFPERR_MISC;
+        goto error;
+    }
+
+    gcry_prime_release_factors(factors);
+
     return 0;
 
 error:
-    gcry_prime_release_factors (factors);
-    gcry_mpi_release (g);
-    gcry_mpi_release (prime);
+    gcry_prime_release_factors(factors);
 
     return result;
 }
@@ -254,33 +236,23 @@ static struct pam_conv PAM_conversation = {
 
 
 static int dhx2_setup(void *obj, char *ibuf _U_, size_t ibuflen _U_,
-                     char *rbuf, size_t *rbuflen)
+                      char *rbuf, size_t *rbuflen)
 {
     int ret;
     size_t nwritten;
-    gcry_mpi_t g, Ma;
+    gcry_mpi_t Ma;
     char *Ra_binary = NULL;
 
     *rbuflen = 0;
 
-    p = gcry_mpi_new(0);
-    g = gcry_mpi_new(0);
     Ra = gcry_mpi_new(0);
     Ma = gcry_mpi_new(0);
 
-    /* Generate p and g for DH */
-    ret = dh_params_generate( &p, &g, PRIMEBITS);
-    if (ret != 0) {
-       LOG(log_info, logtype_uams, "DHX2: Couldn't generate p and g");
-       ret = AFPERR_MISC;
-       goto error;
-    }
-
     /* Generate our random number Ra. */
     Ra_binary = calloc(1, PRIMEBITS/8);
     if (Ra_binary == NULL) {
-       ret = AFPERR_MISC;
-       goto error;
+        ret = AFPERR_MISC;
+        goto error;
     }
     gcry_randomize(Ra_binary, PRIMEBITS/8, GCRY_STRONG_RANDOM);
     gcry_mpi_scan(&Ra, GCRYMPI_FMT_USG, Ra_binary, PRIMEBITS/8, NULL);
@@ -302,8 +274,8 @@ static int dhx2_setup(void *obj, char *ibuf _U_, size_t ibuflen _U_,
     /* g is next */
     gcry_mpi_print( GCRYMPI_FMT_USG, (unsigned char *)rbuf, 4, &nwritten, g);
     if (nwritten < 4) {
-       memmove( rbuf+4-nwritten, rbuf, nwritten);
-       memset( rbuf, 0, 4-nwritten);
+        memmove( rbuf+4-nwritten, rbuf, nwritten);
+        memset( rbuf, 0, 4-nwritten);
     }
     rbuf += 4;
     *rbuflen += 4;
@@ -321,17 +293,16 @@ static int dhx2_setup(void *obj, char *ibuf _U_, size_t ibuflen _U_,
     /* Ma */
     gcry_mpi_print( GCRYMPI_FMT_USG, (unsigned char *)rbuf, PRIMEBITS/8, &nwritten, Ma);
     if (nwritten < PRIMEBITS/8) {
-       memmove(rbuf + (PRIMEBITS/8) - nwritten, rbuf, nwritten);
-       memset(rbuf, 0, (PRIMEBITS/8) - nwritten);
+        memmove(rbuf + (PRIMEBITS/8) - nwritten, rbuf, nwritten);
+        memset(rbuf, 0, (PRIMEBITS/8) - nwritten);
     }
     rbuf += PRIMEBITS/8;
     *rbuflen += PRIMEBITS/8;
 
     ret = AFPERR_AUTHCONT;
 
-error:                         /* We exit here anyway */
-    /* We will only need p and Ra later, but mustn't forget to release it ! */
-    gcry_mpi_release(g);
+error:              /* We exit here anyway */
+    /* We will need Ra later, but mustn't forget to release it ! */
     gcry_mpi_release(Ma);
     return ret;
 }
@@ -423,7 +394,6 @@ static int pam_login_ext(void *obj, char *uname, struct passwd **uam_pwd,
 }
 
 /* -------------------------------- */
-
 static int logincont1(void *obj _U_, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
 {
     int ret;
@@ -443,9 +413,9 @@ static int logincont1(void *obj _U_, char *ibuf, size_t ibuflen, char *rbuf, siz
 
     /* Packet size should be: Session ID + Ma + Encrypted client nonce */
     if (ibuflen != 2 + PRIMEBITS/8 + 16) {
-       LOG(log_error, logtype_uams, "DHX2: Paket length not correct");
+        LOG(log_error, logtype_uams, "DHX2: Paket length not correct");
         ret = AFPERR_PARAM;
-       goto error_noctx;
+        goto error_noctx;
     }
 
     /* Skip session id */
@@ -461,20 +431,20 @@ static int logincont1(void *obj _U_, char *ibuf, size_t ibuflen, char *rbuf, siz
     /* We need K in binary form in order to ... */
     K_bin = calloc(1, PRIMEBITS/8);
     if (K_bin == NULL) {
-       ret = AFPERR_MISC;
-       goto error_noctx;
+        ret = AFPERR_MISC;
+        goto error_noctx;
     }
     gcry_mpi_print(GCRYMPI_FMT_USG, K_bin, PRIMEBITS/8, &nwritten, K);
     if (nwritten < PRIMEBITS/8) {
-       memmove(K_bin + PRIMEBITS/8 - nwritten, K_bin, nwritten);
-       memset(K_bin, 0, PRIMEBITS/8 - nwritten);
+        memmove(K_bin + PRIMEBITS/8 - nwritten, K_bin, nwritten);
+        memset(K_bin, 0, PRIMEBITS/8 - nwritten);
     }
 
     /* ... generate the MD5 hash of K. K_MD5hash is what we actually use ! */
     K_MD5hash = calloc(1, K_hash_len = gcry_md_get_algo_dlen(GCRY_MD_MD5));
     if (K_MD5hash == NULL) {
-       ret = AFPERR_MISC;
-       goto error_noctx;
+        ret = AFPERR_MISC;
+        goto error_noctx;
     }
     gcry_md_hash_buffer(GCRY_MD_MD5, K_MD5hash, K_bin, PRIMEBITS/8);
     free(K_bin);
@@ -485,25 +455,25 @@ static int logincont1(void *obj _U_, char *ibuf, size_t ibuflen, char *rbuf, siz
     /* Set up our encryption context. */
     ctxerror = gcry_cipher_open( &ctx, GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC, 0);
     if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
-       ret = AFPERR_MISC;
+        ret = AFPERR_MISC;
         goto error_ctx;
     }
     /* Set key */
     ctxerror = gcry_cipher_setkey(ctx, K_MD5hash, K_hash_len);
     if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
-       ret = AFPERR_MISC;
+        ret = AFPERR_MISC;
         goto error_ctx;
     }
     /* Set the initialization vector for client->server transfer. */
     ctxerror = gcry_cipher_setiv(ctx, dhx_c2siv, sizeof(dhx_c2siv));
     if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
-       ret = AFPERR_MISC;
+        ret = AFPERR_MISC;
         goto error_ctx;
     }
     /* Finally: decrypt client's md5_K(client nonce, C2SIV) inplace */
     ctxerror = gcry_cipher_decrypt(ctx, ibuf, 16, NULL, 0);
     if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
-       ret = AFPERR_MISC;
+        ret = AFPERR_MISC;
         goto error_ctx;
     }
     /* Pull out clients nonce */
@@ -530,14 +500,14 @@ static int logincont1(void *obj _U_, char *ibuf, size_t ibuflen, char *rbuf, siz
     /* Set the initialization vector for server->client transfer. */
     ctxerror = gcry_cipher_setiv(ctx, dhx_s2civ, sizeof(dhx_s2civ));
     if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
-       ret = AFPERR_MISC;
+        ret = AFPERR_MISC;
         goto error_ctx;
     }
     /* Encrypt md5_K(clientNonce+1, serverNonce) inplace */
     ctxerror = gcry_cipher_encrypt(ctx, rbuf, 32, NULL, 0);
     if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
-       ret = AFPERR_MISC;
-       goto error_ctx;
+        ret = AFPERR_MISC;
+        goto error_ctx;
     }
     rbuf += 32;
     *rbuflen += 32;
@@ -555,14 +525,13 @@ exit:
     gcry_mpi_release(K);
     gcry_mpi_release(Mb);
     gcry_mpi_release(Ra);
-    gcry_mpi_release(p);
     gcry_mpi_release(clientNonce);
     return ret;
 }
 
 static int logincont2(void *obj, struct passwd **uam_pwd,
-                     char *ibuf, size_t ibuflen,
-                     char *rbuf _U_, size_t *rbuflen)
+                      char *ibuf, size_t ibuflen,
+                      char *rbuf _U_, size_t *rbuflen)
 {
     int ret;
     int PAM_error;
@@ -617,7 +586,7 @@ static int logincont2(void *obj, struct passwd **uam_pwd,
     gcry_mpi_scan(&retServerNonce, GCRYMPI_FMT_USG, ibuf, 16, NULL);
     gcry_mpi_sub_ui(retServerNonce, retServerNonce, 1);
     if ( gcry_mpi_cmp( serverNonce, retServerNonce) != 0) {
-       /* We're hacked!  */
+        /* We're hacked!  */
         ret = AFPERR_NOTAUTH;
         goto error_ctx;
     }
@@ -631,9 +600,9 @@ static int logincont2(void *obj, struct passwd **uam_pwd,
     ret = AFPERR_NOTAUTH;
     PAM_error = pam_start("netatalk", PAM_username, &PAM_conversation, &pamh);
     if (PAM_error != PAM_SUCCESS) {
-       LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
-           pam_strerror(pamh,PAM_error));
-       goto error_ctx;
+        LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
+            pam_strerror(pamh,PAM_error));
+        goto error_ctx;
     }
 
     /* solaris craps out if PAM_TTY and PAM_RHOST aren't set. */
@@ -641,25 +610,25 @@ static int logincont2(void *obj, struct passwd **uam_pwd,
     pam_set_item(pamh, PAM_RHOST, hostname);
     PAM_error = pam_authenticate(pamh, 0);
     if (PAM_error != PAM_SUCCESS) {
-       if (PAM_error == PAM_MAXTRIES)
-           ret = AFPERR_PWDEXPR;
-       LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
-           pam_strerror(pamh, PAM_error));
-       goto error_ctx;
+        if (PAM_error == PAM_MAXTRIES)
+            ret = AFPERR_PWDEXPR;
+        LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
+            pam_strerror(pamh, PAM_error));
+        goto error_ctx;
     }
 
     PAM_error = pam_acct_mgmt(pamh, 0);
     if (PAM_error != PAM_SUCCESS ) {
-       LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
-           pam_strerror(pamh, PAM_error));
-       if (PAM_error == PAM_NEW_AUTHTOK_REQD)    /* password expired */
-           ret = AFPERR_PWDEXPR;
+        LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
+            pam_strerror(pamh, PAM_error));
+        if (PAM_error == PAM_NEW_AUTHTOK_REQD)    /* password expired */
+            ret = AFPERR_PWDEXPR;
 #ifdef PAM_AUTHTOKEN_REQD
-       else if (PAM_error == PAM_AUTHTOKEN_REQD)
-           ret = AFPERR_PWDCHNG;
+        else if (PAM_error == PAM_AUTHTOKEN_REQD)
+            ret = AFPERR_PWDCHNG;
 #endif
-       else
-           goto error_ctx;
+        else
+            goto error_ctx;
     }
 
 #ifndef PAM_CRED_ESTABLISH
@@ -667,16 +636,16 @@ static int logincont2(void *obj, struct passwd **uam_pwd,
 #endif
     PAM_error = pam_setcred(pamh, PAM_CRED_ESTABLISH);
     if (PAM_error != PAM_SUCCESS) {
-       LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
-           pam_strerror(pamh, PAM_error));
-       goto error_ctx;
+        LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
+            pam_strerror(pamh, PAM_error));
+        goto error_ctx;
     }
 
     PAM_error = pam_open_session(pamh, 0);
     if (PAM_error != PAM_SUCCESS) {
-       LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
-           pam_strerror(pamh, PAM_error));
-       goto error_ctx;
+        LOG(log_info, logtype_uams, "DHX2: PAM_Error: %s",
+            pam_strerror(pamh, PAM_error));
+        goto error_ctx;
     }
 
     memset(ibuf, 0, 256); /* zero out the password */
@@ -692,7 +661,7 @@ error_noctx:
     free(K_MD5hash);
     K_MD5hash=NULL;
     gcry_mpi_release(serverNonce);
-    gcry_mpi_release(retServerNonce);    
+    gcry_mpi_release(retServerNonce);
     return ret;
 }
 
@@ -728,7 +697,7 @@ static void pam_logout(void) {
  * --- Change pwd stuff --- */
 
 static int changepw_1(void *obj, char *uname,
-                     char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
+                      char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
 {
     *rbuflen = 0;
 
@@ -737,15 +706,15 @@ static int changepw_1(void *obj, char *uname,
     return( dhx2_setup(obj, ibuf, ibuflen, rbuf, rbuflen) );
 }
 
-static int changepw_2(void *obj, 
-                     char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
+static int changepw_2(void *obj,
+                      char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
 {
     return( logincont1(obj, ibuf, ibuflen, rbuf, rbuflen) );
 }
 
 static int changepw_3(void *obj _U_,
-                     char *ibuf, size_t ibuflen _U_, 
-                     char *rbuf _U_, size_t *rbuflen _U_)
+                      char *ibuf, size_t ibuflen _U_,
+                      char *rbuf _U_, size_t *rbuflen _U_)
 {
     int ret;
     int PAM_error;
@@ -812,14 +781,14 @@ static int changepw_3(void *obj _U_,
     ibuf += 16;
 
     /* ---- Start pwd changing with PAM --- */
-    ibuf[255] = '\0';          /* For safety */
+    ibuf[255] = '\0';       /* For safety */
     ibuf[511] = '\0';
 
     /* check if new and old password are equal */
     if (memcmp(ibuf, ibuf + 256, 255) == 0) {
-       LOG(log_info, logtype_uams, "DHX2 Chgpwd: new and old password are equal");
+        LOG(log_info, logtype_uams, "DHX2 Chgpwd: new and old password are equal");
         ret = AFPERR_PWDSAME;
-       goto error_ctx;
+        goto error_ctx;
     }
 
     /* Set these things up for the conv function. PAM_username was set in changepw_1 */
@@ -827,7 +796,7 @@ static int changepw_3(void *obj _U_,
     PAM_error = pam_start("netatalk", PAM_username, &PAM_conversation, &lpamh);
     if (PAM_error != PAM_SUCCESS) {
         LOG(log_info, logtype_uams, "DHX2 Chgpwd: PAM error in pam_start");
-       ret = AFPERR_PARAM;
+        ret = AFPERR_PARAM;
         goto error_ctx;
     }
     pam_set_item(lpamh, PAM_TTY, "afpd");
@@ -837,21 +806,21 @@ static int changepw_3(void *obj _U_,
     seteuid(0);
     PAM_error = pam_authenticate(lpamh,0);
     if (PAM_error != PAM_SUCCESS) {
-       LOG(log_info, logtype_uams, "DHX2 Chgpwd: error authenticating with PAM");
-       seteuid(uid);
-       pam_end(lpamh, PAM_error);
-       ret = AFPERR_NOTAUTH;
-       goto error_ctx;
+        LOG(log_info, logtype_uams, "DHX2 Chgpwd: error authenticating with PAM");
+        seteuid(uid);
+        pam_end(lpamh, PAM_error);
+        ret = AFPERR_NOTAUTH;
+        goto error_ctx;
     }
     PAM_password = ibuf;
     PAM_error = pam_chauthtok(lpamh, 0);
     seteuid(uid); /* un-root ourselves. */
     memset(ibuf, 0, 512);
     if (PAM_error != PAM_SUCCESS) {
-       LOG(log_info, logtype_uams, "DHX2 Chgpwd: error changing pw with PAM");
-       pam_end(lpamh, PAM_error);
-       ret = AFPERR_ACCESS;
-       goto error_ctx;
+        LOG(log_info, logtype_uams, "DHX2 Chgpwd: error changing pw with PAM");
+        pam_end(lpamh, PAM_error);
+        ret = AFPERR_ACCESS;
+        goto error_ctx;
     }
     pam_end(lpamh, 0);
     ret = AFP_OK;
@@ -867,8 +836,8 @@ error_noctx:
 }
 
 static int dhx2_changepw(void *obj _U_, char *uname,
-                        struct passwd *pwd _U_, char *ibuf, size_t ibuflen _U_,
-                        char *rbuf _U_, size_t *rbuflen _U_)
+                         struct passwd *pwd _U_, char *ibuf, size_t ibuflen _U_,
+                         char *rbuf _U_, size_t *rbuflen _U_)
 {
     /* We use this to serialize the three incoming FPChangePassword calls */
     static int dhx2_changepw_status = 1;
@@ -877,22 +846,22 @@ static int dhx2_changepw(void *obj _U_, char *uname,
 
     switch (dhx2_changepw_status) {
     case 1:
-       ret = changepw_1( obj, uname, ibuf, ibuflen, rbuf, rbuflen);
-       if ( ret == AFPERR_AUTHCONT)
-           dhx2_changepw_status = 2;
-       break;
+        ret = changepw_1( obj, uname, ibuf, ibuflen, rbuf, rbuflen);
+        if ( ret == AFPERR_AUTHCONT)
+            dhx2_changepw_status = 2;
+        break;
     case 2:
-       ret = changepw_2( obj, ibuf, ibuflen, rbuf, rbuflen);
+        ret = changepw_2( obj, ibuf, ibuflen, rbuf, rbuflen);
         if ( ret == AFPERR_AUTHCONT)
             dhx2_changepw_status = 3;
-       else
-           dhx2_changepw_status = 1;
-       break;
+        else
+            dhx2_changepw_status = 1;
+        break;
     case 3:
-       ret = changepw_3( obj, ibuf, ibuflen, rbuf, rbuflen);
-       dhx2_changepw_status = 1; /* Whether is was succesfull or not: we
-                                    restart anyway !*/
-       break;
+        ret = changepw_3( obj, ibuf, ibuflen, rbuf, rbuflen);
+        dhx2_changepw_status = 1; /* Whether is was succesfull or not: we
+                                     restart anyway !*/
+        break;
     }
     return ret;
 }
@@ -904,6 +873,17 @@ static int uam_setup(const char *path)
         return -1;
     if (uam_register(UAM_SERVER_CHANGEPW, path, "DHX2", dhx2_changepw) < 0)
         return -1;
+
+    p = gcry_mpi_new(0);
+    g = gcry_mpi_new(0);
+
+    LOG(log_debug, logtype_uams, "DHX2: generating mersenne primes");
+    /* Generate p and g for DH */
+    if (dh_params_generate(PRIMEBITS) != 0) {
+        LOG(log_error, logtype_uams, "DHX2: Couldn't generate p and g");
+        return -1;
+    }
+
     return 0;
 }
 
@@ -911,6 +891,9 @@ static void uam_cleanup(void)
 {
     uam_unregister(UAM_SERVER_LOGIN, "DHX2");
     uam_unregister(UAM_SERVER_CHANGEPW, "DHX2");
+
+    gcry_mpi_release(p);
+    gcry_mpi_release(g);
 }
 
 
@@ -928,4 +911,3 @@ UAM_MODULE_EXPORT struct uam_export uams_dhx2_pam = {
 };
 
 #endif /* USE_PAM && UAM_DHX2 */
-
index af941d15cef45b39eae4906c0bdd0db72e5cf1b7..0f07333b1afcabe192c2298dc2b1000b2114e1e7 100644 (file)
@@ -57,7 +57,7 @@
 #define PRIMEBITS 1024
 
 /* hash a number to a 16-bit quantity */
-#define dhxhash(a) ((((unsigned long) (a) >> 8) ^ \
+#define dhxhash(a) ((((unsigned long) (a) >> 8) ^   \
                      (unsigned long) (a)) & 0xffff)
 
 /* Some parameters need be maintained across calls */
@@ -78,7 +78,7 @@ static struct passwd *dhxpwd;
 /*********************************************************
  * Crypto helper func to generate p and g for use in DH.
  * libgcrypt doesn't provide one directly.
- * Algorithm taken from GNUTLS:gnutls_dh_primes.c 
+ * Algorithm taken from GNUTLS:gnutls_dh_primes.c
  *********************************************************/
 
 /**
@@ -105,53 +105,53 @@ dh_params_generate (gcry_mpi_t *ret_p, gcry_mpi_t *ret_g, unsigned int bits) {
     }
 
     if (bits < 256)
-       qbits = bits / 2;
+        qbits = bits / 2;
     else
-       qbits = (bits / 40) + 105;
+        qbits = (bits / 40) + 105;
 
     if (qbits & 1) /* better have an even number */
-       qbits++;
+        qbits++;
 
     /* find a prime number of size bits. */
     do {
-       if (times) {
-           gcry_mpi_release (prime);
-           gcry_prime_release_factors (factors);
-       }
-       err = gcry_prime_generate (&prime, bits, qbits, &factors, NULL, NULL,
-                                  GCRY_STRONG_RANDOM, GCRY_PRIME_FLAG_SPECIAL_FACTOR);
-       if (err != 0) {
-           result = AFPERR_MISC;
-           goto error;
-       }
-       err = gcry_prime_check (prime, 0);
-       times++;
+        if (times) {
+            gcry_mpi_release (prime);
+            gcry_prime_release_factors (factors);
+        }
+        err = gcry_prime_generate (&prime, bits, qbits, &factors, NULL, NULL,
+                                   GCRY_STRONG_RANDOM, GCRY_PRIME_FLAG_SPECIAL_FACTOR);
+        if (err != 0) {
+            result = AFPERR_MISC;
+            goto error;
+        }
+        err = gcry_prime_check (prime, 0);
+        times++;
     } while (err != 0 && times < 10);
-    
+
     if (err != 0) {
-       result = AFPERR_MISC;
-       goto error;
+        result = AFPERR_MISC;
+        goto error;
     }
 
     /* generate the group generator. */
     err = gcry_prime_group_generator (&g, prime, factors, NULL);
     if (err != 0) {
-       result = AFPERR_MISC;
-       goto error;
+        result = AFPERR_MISC;
+        goto error;
     }
-    
+
     gcry_prime_release_factors (factors);
     factors = NULL;
-    
+
     if (ret_g)
-       *ret_g = g;
+        *ret_g = g;
     else
-       gcry_mpi_release (g);
+        gcry_mpi_release (g);
     if (ret_p)
-       *ret_p = prime;
+        *ret_p = prime;
     else
-       gcry_mpi_release (prime);
-    
+        gcry_mpi_release (prime);
+
     return 0;
 
 error:
@@ -163,7 +163,7 @@ error:
 }
 
 static int dhx2_setup(void *obj, char *ibuf _U_, size_t ibuflen _U_,
-                     char *rbuf, size_t *rbuflen)
+                      char *rbuf, size_t *rbuflen)
 {
     int ret;
     size_t nwritten;
@@ -185,7 +185,7 @@ static int dhx2_setup(void *obj, char *ibuf _U_, size_t ibuflen _U_,
 #endif /* SHADOWPW */
 
     if (!dhxpwd->pw_passwd)
-       return AFPERR_NOTAUTH;
+        return AFPERR_NOTAUTH;
 
     /* Initialize DH params */
 
@@ -197,16 +197,16 @@ static int dhx2_setup(void *obj, char *ibuf _U_, size_t ibuflen _U_,
     /* Generate p and g for DH */
     ret = dh_params_generate( &p, &g, PRIMEBITS);
     if (ret != 0) {
-       LOG(log_info, logtype_uams, "DHX2: Couldn't generate p and g");
-       ret = AFPERR_MISC;
-       goto error;
+        LOG(log_info, logtype_uams, "DHX2: Couldn't generate p and g");
+        ret = AFPERR_MISC;
+        goto error;
     }
 
     /* Generate our random number Ra. */
     Ra_binary = calloc(1, PRIMEBITS/8);
     if (Ra_binary == NULL) {
-       ret = AFPERR_MISC;
-       goto error;
+        ret = AFPERR_MISC;
+        goto error;
     }
     gcry_randomize(Ra_binary, PRIMEBITS/8, GCRY_STRONG_RANDOM);
     gcry_mpi_scan(&Ra, GCRYMPI_FMT_USG, Ra_binary, PRIMEBITS/8, NULL);
@@ -228,8 +228,8 @@ static int dhx2_setup(void *obj, char *ibuf _U_, size_t ibuflen _U_,
     /* g is next */
     gcry_mpi_print( GCRYMPI_FMT_USG, (unsigned char *)rbuf, 4, &nwritten, g);
     if (nwritten < 4) {
-       memmove( rbuf+4-nwritten, rbuf, nwritten);
-       memset( rbuf, 0, 4-nwritten);
+        memmove( rbuf+4-nwritten, rbuf, nwritten);
+        memset( rbuf, 0, 4-nwritten);
     }
     rbuf += 4;
     *rbuflen += 4;
@@ -247,15 +247,15 @@ static int dhx2_setup(void *obj, char *ibuf _U_, size_t ibuflen _U_,
     /* Ma */
     gcry_mpi_print( GCRYMPI_FMT_USG, (unsigned char *)rbuf, PRIMEBITS/8, &nwritten, Ma);
     if (nwritten < PRIMEBITS/8) {
-       memmove(rbuf + (PRIMEBITS/8) - nwritten, rbuf, nwritten);
-       memset(rbuf, 0, (PRIMEBITS/8) - nwritten);
+        memmove(rbuf + (PRIMEBITS/8) - nwritten, rbuf, nwritten);
+        memset(rbuf, 0, (PRIMEBITS/8) - nwritten);
     }
     rbuf += PRIMEBITS/8;
     *rbuflen += PRIMEBITS/8;
 
     ret = AFPERR_AUTHCONT;
 
-error:                         /* We exit here anyway */
+error:              /* We exit here anyway */
     /* We will only need p and Ra later, but mustn't forget to release it ! */
     gcry_mpi_release(g);
     gcry_mpi_release(Ma);
@@ -280,8 +280,8 @@ static int login(void *obj, char *username, int ulen,  struct passwd **uam_pwd _
 /* dhx login: things are done in a slightly bizarre order to avoid
  * having to clean things up if there's an error. */
 static int passwd_login(void *obj, struct passwd **uam_pwd,
-                     char *ibuf, size_t ibuflen,
-                     char *rbuf, size_t *rbuflen)
+                        char *ibuf, size_t ibuflen,
+                        char *rbuf, size_t *rbuflen)
 {
     char *username;
     size_t len, ulen;
@@ -314,8 +314,8 @@ static int passwd_login(void *obj, struct passwd **uam_pwd,
 
 /* ----------------------------- */
 static int passwd_login_ext(void *obj, char *uname, struct passwd **uam_pwd,
-                         char *ibuf, size_t ibuflen,
-                         char *rbuf, size_t *rbuflen)
+                            char *ibuf, size_t ibuflen,
+                            char *rbuf, size_t *rbuflen)
 {
     char *username;
     size_t len, ulen;
@@ -350,8 +350,8 @@ static int passwd_login_ext(void *obj, char *uname, struct passwd **uam_pwd,
 /* -------------------------------- */
 
 static int logincont1(void *obj _U_, struct passwd **uam_pwd _U_,
-                         char *ibuf, size_t ibuflen,
-                         char *rbuf, size_t *rbuflen)
+                      char *ibuf, size_t ibuflen,
+                      char *rbuf, size_t *rbuflen)
 {
     size_t nwritten;
     int ret;
@@ -370,9 +370,9 @@ static int logincont1(void *obj _U_, struct passwd **uam_pwd _U_,
 
     /* Packet size should be: Session ID + Ma + Encrypted client nonce */
     if (ibuflen != 2 + PRIMEBITS/8 + 16) {
-       LOG(log_error, logtype_uams, "DHX2: Paket length not correct");
+        LOG(log_error, logtype_uams, "DHX2: Paket length not correct");
         ret = AFPERR_PARAM;
-       goto error_noctx;
+        goto error_noctx;
     }
 
     /* Skip session id */
@@ -388,49 +388,51 @@ static int logincont1(void *obj _U_, struct passwd **uam_pwd _U_,
     /* We need K in binary form in order to ... */
     K_bin = calloc(1, PRIMEBITS/8);
     if (K_bin == NULL) {
-       ret = AFPERR_MISC;
-       goto error_noctx;
+        ret = AFPERR_MISC;
+        goto error_noctx;
     }
     gcry_mpi_print(GCRYMPI_FMT_USG, K_bin, PRIMEBITS/8, &nwritten, K);
     if (nwritten < PRIMEBITS/8) {
-       memmove(K_bin + PRIMEBITS/8 - nwritten, K_bin, nwritten);
-       memset(K_bin, 0, PRIMEBITS/8 - nwritten);
+        memmove(K_bin + PRIMEBITS/8 - nwritten, K_bin, nwritten);
+        memset(K_bin, 0, PRIMEBITS/8 - nwritten);
     }
 
     /* ... generate the MD5 hash of K. K_MD5hash is what we actually use ! */
     K_MD5hash = calloc(1, K_hash_len = gcry_md_get_algo_dlen(GCRY_MD_MD5));
     if (K_MD5hash == NULL) {
-       ret = AFPERR_MISC;
-       goto error_noctx;
+        ret = AFPERR_MISC;
+        free(K_bin);
+        K_bin = NULL;
+        goto error_noctx;
     }
     gcry_md_hash_buffer(GCRY_MD_MD5, K_MD5hash, K_bin, PRIMEBITS/8);
-    free(K_bin); 
-    K_bin = NULL; 
+    free(K_bin);
+    K_bin = NULL;
 
     /* FIXME: To support the Reconnect UAM, we need to store this key somewhere */
 
     /* Set up our encryption context. */
     ctxerror = gcry_cipher_open( &ctx, GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC, 0);
     if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
-       ret = AFPERR_MISC;
+        ret = AFPERR_MISC;
         goto error_ctx;
     }
     /* Set key */
     ctxerror = gcry_cipher_setkey(ctx, K_MD5hash, K_hash_len);
     if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
-       ret = AFPERR_MISC;
+        ret = AFPERR_MISC;
         goto error_ctx;
     }
     /* Set the initialization vector for client->server transfer. */
     ctxerror = gcry_cipher_setiv(ctx, dhx_c2siv, sizeof(dhx_c2siv));
     if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
-       ret = AFPERR_MISC;
+        ret = AFPERR_MISC;
         goto error_ctx;
     }
     /* Finally: decrypt client's md5_K(client nonce, C2SIV) inplace */
     ctxerror = gcry_cipher_decrypt(ctx, ibuf, 16, NULL, 0);
     if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
-       ret = AFPERR_MISC;
+        ret = AFPERR_MISC;
         goto error_ctx;
     }
     /* Pull out clients nonce */
@@ -457,14 +459,14 @@ static int logincont1(void *obj _U_, struct passwd **uam_pwd _U_,
     /* Set the initialization vector for server->client transfer. */
     ctxerror = gcry_cipher_setiv(ctx, dhx_s2civ, sizeof(dhx_s2civ));
     if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
-       ret = AFPERR_MISC;
+        ret = AFPERR_MISC;
         goto error_ctx;
     }
     /* Encrypt md5_K(clientNonce+1, serverNonce) inplace */
     ctxerror = gcry_cipher_encrypt(ctx, rbuf, 32, NULL, 0);
     if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
-       ret = AFPERR_MISC;
-       goto error_ctx;
+        ret = AFPERR_MISC;
+        goto error_ctx;
     }
     rbuf += 32;
     *rbuflen += 32;
@@ -488,8 +490,8 @@ exit:
 }
 
 static int logincont2(void *obj _U_, struct passwd **uam_pwd,
-                     char *ibuf, size_t ibuflen,
-                     char *rbuf _U_, size_t *rbuflen)
+                      char *ibuf, size_t ibuflen,
+                      char *rbuf _U_, size_t *rbuflen)
 {
 #ifdef SHADOWPW
     struct spwd *sp;
@@ -501,6 +503,8 @@ static int logincont2(void *obj _U_, struct passwd **uam_pwd,
     gcry_error_t ctxerror;
 
     *rbuflen = 0;
+    retServerNonce = gcry_mpi_new(0);
+
     /* Packet size should be: Session ID + ServerNonce + Passwd buffer (evantually +10 extra bytes, see Apples Docs)*/
     if ((ibuflen != 2 + 16 + 256) && (ibuflen != 2 + 16 + 256 + 10)) {
         LOG(log_error, logtype_uams, "DHX2: Paket length not correct: %d. Should be 274 or 284.", ibuflen);
@@ -508,8 +512,6 @@ static int logincont2(void *obj _U_, struct passwd **uam_pwd,
         goto error_noctx;
     }
 
-    retServerNonce = gcry_mpi_new(0);
-
     /* Set up our encryption context. */
     ctxerror = gcry_cipher_open( &ctx, GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC, 0);
     if (gcry_err_code(ctxerror) != GPG_ERR_NO_ERROR) {
@@ -542,11 +544,11 @@ static int logincont2(void *obj _U_, struct passwd **uam_pwd,
     gcry_mpi_scan(&retServerNonce, GCRYMPI_FMT_USG, ibuf, 16, NULL);
     gcry_mpi_sub_ui(retServerNonce, retServerNonce, 1);
     if ( gcry_mpi_cmp( serverNonce, retServerNonce) != 0) {
-       /* We're hacked!  */
+        /* We're hacked!  */
         ret = AFPERR_NOTAUTH;
         goto error_ctx;
     }
-    ibuf += 16;                        /* ibuf now point to passwd in cleartext */
+    ibuf += 16;         /* ibuf now point to passwd in cleartext */
 
     /* ---- Start authentication --- */
     ret = AFPERR_NOTAUTH;
@@ -554,15 +556,15 @@ static int logincont2(void *obj _U_, struct passwd **uam_pwd,
     p = crypt( ibuf, dhxpwd->pw_passwd );
     memset(ibuf, 0, 255);
     if ( strcmp( p, dhxpwd->pw_passwd ) == 0 ) {
-       *uam_pwd = dhxpwd;
-       ret = AFP_OK;
+        *uam_pwd = dhxpwd;
+        ret = AFP_OK;
     }
 
 #ifdef SHADOWPW
     if (( sp = getspnam( dhxpwd->pw_name )) == NULL ) {
         LOG(log_info, logtype_uams, "no shadow passwd entry for %s", dhxpwd->pw_name);
         ret = AFPERR_NOTAUTH;
-       goto exit;
+        goto exit;
     }
 
     /* check for expired password */
@@ -570,9 +572,9 @@ static int logincont2(void *obj _U_, struct passwd **uam_pwd,
         time_t now = time(NULL) / (60*60*24);
         int32_t expire_days = sp->sp_lstchg - now + sp->sp_max;
         if ( expire_days < 0 ) {
-           LOG(log_info, logtype_uams, "password for user %s expired", dhxpwd->pw_name);
-           ret = AFPERR_PWDEXPR;
-           goto exit;
+            LOG(log_info, logtype_uams, "password for user %s expired", dhxpwd->pw_name);
+            ret = AFPERR_PWDEXPR;
+            goto exit;
         }
     }
 #endif /* SHADOWPW */
@@ -589,8 +591,8 @@ exit:
 }
 
 static int passwd_logincont(void *obj, struct passwd **uam_pwd,
-                         char *ibuf, size_t ibuflen,
-                         char *rbuf, size_t *rbuflen)
+                            char *ibuf, size_t ibuflen,
+                            char *rbuf, size_t *rbuflen)
 {
     u_int16_t retID;
     int ret;
index 690005640a0dff0ec9449e2121798a8998302367..2bf09187b4c6cb996e3a729d0e9c776cabde0311 100644 (file)
@@ -4,8 +4,8 @@ atalkincludedir = $(includedir)/atalk
 atalkinclude_HEADERS = \
        adouble.h vfs.h aep.h afp.h asp.h atp.h boolean.h \
        cnid.h compat.h ddp.h dsi.h ldapconfig.h list.h logger.h \
-       nbp.h netddp.h pap.h paths.h rtmp.h server_child.h \
+       nbp.h netddp.h pap.h paths.h queue.h rtmp.h server_child.h \
        server_ipc.h tdb.h uam.h unicode.h util.h uuid.h volinfo.h \
        zip.h ea.h acl.h unix.h directory.h hash.h volume.h
 
-noinst_HEADERS = cnid_dbd_private.h cnid_private.h
+noinst_HEADERS = cnid_dbd_private.h cnid_private.h bstradd.h bstrlib.h errchk.h ftw.h
index edb391598517ddb757f57607719cffd4cfdf340b..53d10b19aa011383c1d8f40788f4984332e70b18 100644 (file)
@@ -1,5 +1,4 @@
 /*
-   $Id: acl.h,v 1.1 2009-10-14 15:04:01 franklahm Exp $
    Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
 
    This program is free software; you can redistribute it and/or modify
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
 
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
+
+#ifdef HAVE_SOLARIS_ACLS
+#include <sys/acl.h>
+#endif  /* HAVE_SOLARIS_ACLS */
+
+#ifdef HAVE_POSIX_ACLS
+#include <sys/types.h>
 #include <sys/acl.h>
-#endif  /* HAVE_NFSv4_ACLS */
+#endif /* HAVE_POSIX_ACLS */
 
-/* Solaris NFSv4 ACL stuff */
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_SOLARIS_ACLS
+#define chmod nfsv4_chmod
 extern int get_nfsv4_acl(const char *name, ace_t **retAces);
-extern int remove_acl(const char *name);
-#endif /* HAVE_NFSv4_ACLS */
+extern int strip_trivial_aces(ace_t **saces, int sacecount);
+extern int strip_nontrivial_aces(ace_t **saces, int sacecount);
+extern ace_t *concat_aces(ace_t *aces1, int ace1count, ace_t *aces2, int ace2count);
+extern int nfsv4_chmod(char *name, mode_t mode);
+#endif /* HAVE_SOLARIS_ACLS */
+
+extern int remove_acl_vfs(const char *name);
 
+#endif /* HAVE_ACLS */
 
-#endif  /* ATALK_ACL_H */
+#endif /* ATALK_ACL_H */
index b47464ef55106142e3443e29b21ee1b7a08c87f7..18422eca680dc89c2950813d36cfe7ff97b50073 100644 (file)
   need _XOPEN_SOURCE defined for pread.
 */
 #if defined(HAVE_PREAD) && !defined(SOLARIS) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(TRU64)
-#ifdef _XOPEN_SOURCE
-#undef _XOPEN_SOURCE
-#endif
+#ifndef _XOPEN_SOURCE
 #define _XOPEN_SOURCE 500
 #endif
+#endif
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -78,6 +77,7 @@
 #endif
 
 #include <sys/mman.h>
+
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
@@ -549,7 +549,7 @@ extern void *ad_mmapwrite (struct adouble *, const u_int32_t,
 #define ad_munmap(buf, len)  (munmap((buf), (len)))
 
 /* ad_date.c */
-extern int ad_setdate (const struct adouble *, unsigned int, u_int32_t);
+extern int ad_setdate (struct adouble *, unsigned int, u_int32_t);
 extern int ad_getdate (const struct adouble *, unsigned int, u_int32_t *);
 
 /* ad_attr.c */
index b93eb8bce4ea0dd129ee25362ee8f4268cc0f57e..303e705047f58fa5b7b88a799222034dcb282670 100644 (file)
@@ -43,20 +43,19 @@ typedef u_int16_t AFPUserBytes;
 #define AFPTRANS_ALL          (AFPTRANS_DDP | AFPTRANS_TCP)
 
 /* server flags */
-#define AFPSRVRINFO_COPY        (1<<0)  /* supports copyfile */
-#define AFPSRVRINFO_PASSWD      (1<<1)  /* supports change password */
+#define AFPSRVRINFO_COPY         (1<<0)  /* supports copyfile */
+#define AFPSRVRINFO_PASSWD       (1<<1)  /* supports change password */
 #define AFPSRVRINFO_NOSAVEPASSWD (1<<2)  /* don't allow save password */
 #define AFPSRVRINFO_SRVMSGS      (1<<3)  /* supports server messages */
 #define AFPSRVRINFO_SRVSIGNATURE (1<<4)  /* supports server signature */
 #define AFPSRVRINFO_TCPIP        (1<<5)  /* supports tcpip */
 #define AFPSRVRINFO_SRVNOTIFY    (1<<6)  /* supports server notifications */ 
-
 #define AFPSRVRINFO_SRVRECONNECT (1<<7)  /* supports server reconnect */ 
 #define AFPSRVRINFO_SRVRDIR      (1<<8)  /* supports directories service */ 
-
 #define AFPSRVRINFO_SRVUTF8      (1<<9)  /* supports UTF8 names AFP 3.1 */ 
 #define AFPSRVRINFO_UUID         (1<<10) /* supports UUIDs */
-#define AFPSRVRINFO_FASTBOZO    (1<<15) /* fast copying */
+#define AFPSRVRINFO_EXTSLEEP     (1<<11) /* supports extended sleep */
+#define AFPSRVRINFO_FASTBOZO     (1<<15) /* fast copying */
 
 #define AFP_OK         0
 #define AFPERR_DID1     -4000   /* not an afp error DID is 1*/
@@ -129,6 +128,11 @@ typedef enum {
   AFPMESG_SERVER = 1
 } afpmessage_t;
 
+/* extended sleep flag */
+#define AFPZZZ_EXT_SLEEP  1
+#define AFPZZZ_EXT_WAKEUP 2
+
+
 /* AFP functions */
 #define AFP_BYTELOCK        1
 #define AFP_CLOSEVOL            2
@@ -213,4 +217,7 @@ typedef enum {
 #define AFP_SETACL              74
 #define AFP_ACCESS              75
 
+/* more defines */
+#define REPLAYCACHE_SIZE 128
+
 #endif
diff --git a/include/atalk/bstradd.h b/include/atalk/bstradd.h
new file mode 100644 (file)
index 0000000..310349b
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+   Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+   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.
+*/
+
+/*!
+ * @file
+ * Additional functions for bstrlib.
+ */
+
+#ifndef ATALK_BSTRADD_H
+#define ATALK_BSTRADD_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <atalk/bstrlib.h>
+
+#define cfrombstr(b) ((char *)((b)->data))
+
+/* strip slashes from end of a bstring */
+#define BSTRING_STRIP_SLASH(a)                      \
+    do {                                            \
+        while (bchar((a), blength(a) - 1) == '/')   \
+            bdelete((a), blength(a) - 1, 1);        \
+    } while (0);
+
+typedef struct tagbstring static_bstring;
+
+extern bstring brefcstr(const char *str);
+extern int bunrefcstr(bstring b);
+
+extern struct bstrList *bstrListCreateMin(int min);
+extern int bstrListPush(struct bstrList *sl, bstring bs);
+extern bstring bstrListPop(struct bstrList *sl);
+extern bstring bjoinInv(const struct bstrList * bl, const_bstring sep);
+#endif /* ATALK_BSTRADD_H */
diff --git a/include/atalk/bstrlib.h b/include/atalk/bstrlib.h
new file mode 100644 (file)
index 0000000..0cd3719
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * This source file is part of the bstring string library.  This code was
+ * written by Paul Hsieh in 2002-2008, and is covered by the BSD open source 
+ * license and the GPL. Refer to the accompanying documentation for details 
+ * on usage and license.
+ */
+
+/*!
+ * @file
+ * This file is the core module for implementing the bstring functions.
+ */
+
+#ifndef BSTRLIB_INCLUDE
+#define BSTRLIB_INCLUDE
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+
+#if !defined (BSTRLIB_VSNP_OK) && !defined (BSTRLIB_NOVSNP)
+# if defined (__TURBOC__) && !defined (__BORLANDC__)
+#  define BSTRLIB_NOVSNP
+# endif
+#endif
+
+#define BSTR_ERR (-1)
+#define BSTR_OK (0)
+#define BSTR_BS_BUFF_LENGTH_GET (0)
+
+typedef struct tagbstring * bstring;
+typedef const struct tagbstring * const_bstring;
+
+/* Copy functions */
+#define cstr2bstr bfromcstr
+extern bstring bfromcstr (const char * str);
+extern bstring bfromcstralloc (int mlen, const char * str);
+extern bstring blk2bstr (const void * blk, int len);
+extern char * bstr2cstr (const_bstring s, char z);
+extern int bcstrfree (char * s);
+extern bstring bstrcpy (const_bstring b1);
+extern int bassign (bstring a, const_bstring b);
+extern int bassignmidstr (bstring a, const_bstring b, int left, int len);
+extern int bassigncstr (bstring a, const char * str);
+extern int bassignblk (bstring a, const void * s, int len);
+
+/* Destroy function */
+extern int bdestroy (bstring b);
+
+/* Space allocation hinting functions */
+extern int balloc (bstring s, int len);
+extern int ballocmin (bstring b, int len);
+
+/* Substring extraction */
+extern bstring bmidstr (const_bstring b, int left, int len);
+
+/* Various standard manipulations */
+extern int bconcat (bstring b0, const_bstring b1);
+extern int bconchar (bstring b0, char c);
+extern int bcatcstr (bstring b, const char * s);
+extern int bcatblk (bstring b, const void * s, int len);
+extern int binsert (bstring s1, int pos, const_bstring s2, unsigned char fill);
+extern int binsertch (bstring s1, int pos, int len, unsigned char fill);
+extern int breplace (bstring b1, int pos, int len, const_bstring b2, unsigned char fill);
+extern int bdelete (bstring s1, int pos, int len);
+extern int bsetstr (bstring b0, int pos, const_bstring b1, unsigned char fill);
+extern int btrunc (bstring b, int n);
+
+/* Scan/search functions */
+extern int bstricmp (const_bstring b0, const_bstring b1);
+extern int bstrnicmp (const_bstring b0, const_bstring b1, int n);
+extern int biseqcaseless (const_bstring b0, const_bstring b1);
+extern int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len);
+extern int biseq (const_bstring b0, const_bstring b1);
+extern int bisstemeqblk (const_bstring b0, const void * blk, int len);
+extern int biseqcstr (const_bstring b, const char * s);
+extern int biseqcstrcaseless (const_bstring b, const char * s);
+extern int bstrcmp (const_bstring b0, const_bstring b1);
+extern int bstrncmp (const_bstring b0, const_bstring b1, int n);
+extern int binstr (const_bstring s1, int pos, const_bstring s2);
+extern int binstrr (const_bstring s1, int pos, const_bstring s2);
+extern int binstrcaseless (const_bstring s1, int pos, const_bstring s2);
+extern int binstrrcaseless (const_bstring s1, int pos, const_bstring s2);
+extern int bstrchrp (const_bstring b, int c, int pos);
+extern int bstrrchrp (const_bstring b, int c, int pos);
+#define bstrchr(b,c) bstrchrp ((b), (c), 0)
+#define bstrrchr(b,c) bstrrchrp ((b), (c), blength(b)-1)
+extern int binchr (const_bstring b0, int pos, const_bstring b1);
+extern int binchrr (const_bstring b0, int pos, const_bstring b1);
+extern int bninchr (const_bstring b0, int pos, const_bstring b1);
+extern int bninchrr (const_bstring b0, int pos, const_bstring b1);
+extern int bfindreplace (bstring b, const_bstring find, const_bstring repl, int pos);
+extern int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl, int pos);
+
+/* List of string container functions */
+struct bstrList {
+    int qty, mlen;
+    bstring * entry;
+};
+extern struct bstrList * bstrListCreate (void);
+extern int bstrListDestroy (struct bstrList * sl);
+extern int bstrListAlloc (struct bstrList * sl, int msz);
+extern int bstrListAllocMin (struct bstrList * sl, int msz);
+
+/* String split and join functions */
+extern struct bstrList * bsplit (const_bstring str, unsigned char splitChar);
+extern struct bstrList * bsplits (const_bstring str, const_bstring splitStr);
+extern struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr);
+extern bstring bjoin (const struct bstrList * bl, const_bstring sep);
+extern int bsplitcb (const_bstring str, unsigned char splitChar, int pos,
+       int (* cb) (void * parm, int ofs, int len), void * parm);
+extern int bsplitscb (const_bstring str, const_bstring splitStr, int pos,
+       int (* cb) (void * parm, int ofs, int len), void * parm);
+extern int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos,
+       int (* cb) (void * parm, int ofs, int len), void * parm);
+
+/* Miscellaneous functions */
+extern int bpattern (bstring b, int len);
+extern int btoupper (bstring b);
+extern int btolower (bstring b);
+extern int bltrimws (bstring b);
+extern int brtrimws (bstring b);
+extern int btrimws (bstring b);
+
+#if !defined (BSTRLIB_NOVSNP)
+extern bstring bformat (const char * fmt, ...);
+extern int bformata (bstring b, const char * fmt, ...);
+extern int bassignformat (bstring b, const char * fmt, ...);
+extern int bvcformata (bstring b, int count, const char * fmt, va_list arglist);
+
+#define bvformata(ret, b, fmt, lastarg) { \
+bstring bstrtmp_b = (b); \
+const char * bstrtmp_fmt = (fmt); \
+int bstrtmp_r = BSTR_ERR, bstrtmp_sz = 16; \
+       for (;;) { \
+               va_list bstrtmp_arglist; \
+               va_start (bstrtmp_arglist, lastarg); \
+               bstrtmp_r = bvcformata (bstrtmp_b, bstrtmp_sz, bstrtmp_fmt, bstrtmp_arglist); \
+               va_end (bstrtmp_arglist); \
+               if (bstrtmp_r >= 0) { /* Everything went ok */ \
+                       bstrtmp_r = BSTR_OK; \
+                       break; \
+               } else if (-bstrtmp_r <= bstrtmp_sz) { /* A real error? */ \
+                       bstrtmp_r = BSTR_ERR; \
+                       break; \
+               } \
+               bstrtmp_sz = -bstrtmp_r; /* Doubled or target size */ \
+       } \
+       ret = bstrtmp_r; \
+}
+
+#endif
+
+typedef int (*bNgetc) (void *parm);
+typedef size_t (* bNread) (void *buff, size_t elsize, size_t nelem, void *parm);
+
+/* Input functions */
+extern bstring bgetstream (bNgetc getcPtr, void * parm, char terminator);
+extern bstring bread (bNread readPtr, void * parm);
+extern int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator);
+extern int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator);
+extern int breada (bstring b, bNread readPtr, void * parm);
+
+/* Stream functions */
+extern struct bStream * bsopen (bNread readPtr, void * parm);
+extern void * bsclose (struct bStream * s);
+extern int bsbufflength (struct bStream * s, int sz);
+extern int bsreadln (bstring b, struct bStream * s, char terminator);
+extern int bsreadlns (bstring r, struct bStream * s, const_bstring term);
+extern int bsread (bstring b, struct bStream * s, int n);
+extern int bsreadlna (bstring b, struct bStream * s, char terminator);
+extern int bsreadlnsa (bstring r, struct bStream * s, const_bstring term);
+extern int bsreada (bstring b, struct bStream * s, int n);
+extern int bsunread (struct bStream * s, const_bstring b);
+extern int bspeek (bstring r, const struct bStream * s);
+extern int bssplitscb (struct bStream * s, const_bstring splitStr, 
+       int (* cb) (void * parm, int ofs, const_bstring entry), void * parm);
+extern int bssplitstrcb (struct bStream * s, const_bstring splitStr, 
+       int (* cb) (void * parm, int ofs, const_bstring entry), void * parm);
+extern int bseof (const struct bStream * s);
+
+struct tagbstring {
+       int mlen;
+       int slen;
+       unsigned char * data;
+};
+
+/* Accessor macros */
+#define blengthe(b, e)      (((b) == (void *)0 || (b)->slen < 0) ? (int)(e) : ((b)->slen))
+#define blength(b)          (blengthe ((b), 0))
+#define bdataofse(b, o, e)  (((b) == (void *)0 || (b)->data == (void*)0) ? (char *)(e) : ((char *)(b)->data) + (o))
+#define bdataofs(b, o)      (bdataofse ((b), (o), (void *)0))
+#define bdatae(b, e)        (bdataofse (b, 0, e))
+#define bdata(b)            (bdataofs (b, 0))
+#define bchare(b, p, e)     ((((unsigned)(p)) < (unsigned)blength(b)) ? ((b)->data[(p)]) : (e))
+#define bchar(b, p)         bchare ((b), (p), '\0')
+
+/* Static constant string initialization macro */
+#define bsStaticMlen(q,m)   {(m), (int) sizeof(q)-1, (unsigned char *) ("" q "")}
+#if defined(_MSC_VER)
+# define bsStatic(q)        bsStaticMlen(q,-32)
+#endif
+#ifndef bsStatic
+# define bsStatic(q)        bsStaticMlen(q,-__LINE__)
+#endif
+
+/* Static constant block parameter pair */
+#define bsStaticBlkParms(q) ((void *)("" q "")), ((int) sizeof(q)-1)
+
+/* Reference building macros */
+#define cstr2tbstr btfromcstr
+#define btfromcstr(t,s) {                                            \
+    (t).data = (unsigned char *) (s);                                \
+    (t).slen = ((t).data) ? ((int) (strlen) ((char *)(t).data)) : 0; \
+    (t).mlen = -1;                                                   \
+}
+#define blk2tbstr(t,s,l) {            \
+    (t).data = (unsigned char *) (s); \
+    (t).slen = l;                     \
+    (t).mlen = -1;                    \
+}
+#define btfromblk(t,s,l) blk2tbstr(t,s,l)
+#define bmid2tbstr(t,b,p,l) {                                                \
+    const_bstring bstrtmp_s = (b);                                           \
+    if (bstrtmp_s && bstrtmp_s->data && bstrtmp_s->slen >= 0) {              \
+        int bstrtmp_left = (p);                                              \
+        int bstrtmp_len  = (l);                                              \
+        if (bstrtmp_left < 0) {                                              \
+            bstrtmp_len += bstrtmp_left;                                     \
+            bstrtmp_left = 0;                                                \
+        }                                                                    \
+        if (bstrtmp_len > bstrtmp_s->slen - bstrtmp_left)                    \
+            bstrtmp_len = bstrtmp_s->slen - bstrtmp_left;                    \
+        if (bstrtmp_len <= 0) {                                              \
+            (t).data = (unsigned char *)"";                                  \
+            (t).slen = 0;                                                    \
+        } else {                                                             \
+            (t).data = bstrtmp_s->data + bstrtmp_left;                       \
+            (t).slen = bstrtmp_len;                                          \
+        }                                                                    \
+    } else {                                                                 \
+        (t).data = (unsigned char *)"";                                      \
+        (t).slen = 0;                                                        \
+    }                                                                        \
+    (t).mlen = -__LINE__;                                                    \
+}
+#define btfromblkltrimws(t,s,l) {                                            \
+    int bstrtmp_idx = 0, bstrtmp_len = (l);                                  \
+    unsigned char * bstrtmp_s = (s);                                         \
+    if (bstrtmp_s && bstrtmp_len >= 0) {                                     \
+        for (; bstrtmp_idx < bstrtmp_len; bstrtmp_idx++) {                   \
+            if (!isspace (bstrtmp_s[bstrtmp_idx])) break;                    \
+        }                                                                    \
+    }                                                                        \
+    (t).data = bstrtmp_s + bstrtmp_idx;                                      \
+    (t).slen = bstrtmp_len - bstrtmp_idx;                                    \
+    (t).mlen = -__LINE__;                                                    \
+}
+#define btfromblkrtrimws(t,s,l) {                                            \
+    int bstrtmp_len = (l) - 1;                                               \
+    unsigned char * bstrtmp_s = (s);                                         \
+    if (bstrtmp_s && bstrtmp_len >= 0) {                                     \
+        for (; bstrtmp_len >= 0; bstrtmp_len--) {                            \
+            if (!isspace (bstrtmp_s[bstrtmp_len])) break;                    \
+        }                                                                    \
+    }                                                                        \
+    (t).data = bstrtmp_s;                                                    \
+    (t).slen = bstrtmp_len + 1;                                              \
+    (t).mlen = -__LINE__;                                                    \
+}
+#define btfromblktrimws(t,s,l) {                                             \
+    int bstrtmp_idx = 0, bstrtmp_len = (l) - 1;                              \
+    unsigned char * bstrtmp_s = (s);                                         \
+    if (bstrtmp_s && bstrtmp_len >= 0) {                                     \
+        for (; bstrtmp_idx <= bstrtmp_len; bstrtmp_idx++) {                  \
+            if (!isspace (bstrtmp_s[bstrtmp_idx])) break;                    \
+        }                                                                    \
+        for (; bstrtmp_len >= bstrtmp_idx; bstrtmp_len--) {                  \
+            if (!isspace (bstrtmp_s[bstrtmp_len])) break;                    \
+        }                                                                    \
+    }                                                                        \
+    (t).data = bstrtmp_s + bstrtmp_idx;                                      \
+    (t).slen = bstrtmp_len + 1 - bstrtmp_idx;                                \
+    (t).mlen = -__LINE__;                                                    \
+}
+
+/* Write protection macros */
+#define bwriteprotect(t)     { if ((t).mlen >=  0) (t).mlen = -1; }
+#define bwriteallow(t)       { if ((t).mlen == -1) (t).mlen = (t).slen + ((t).slen == 0); }
+#define biswriteprotected(t) ((t).mlen <= 0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
index 8dcb33fbcae5bdb89d038dbd954f9b7aa29025c3..d7df70e1d164ddd82c5c61f44f9e07996500607a 100644 (file)
@@ -1,9 +1,8 @@
-/* 
- * $Id: cnid.h,v 1.15 2010-03-31 09:47:32 franklahm Exp $
- *
+/*
  * Copyright (c) 2003 the Netatalk Team
  * Copyright (c) 2003 Rafal Lewczuk <rlewczuk@pronet.pl>
- * 
+ * Copyright (c) 2010 Frank Lahm
+ *
  * This program is free software; you can redistribute and/or modify
  * it under the terms of the GNU General Public License as published
  * by the Free Software Foundation version 2 of the License or later
  *
  */
 
-/* 
- * This file contains all generic CNID related stuff 
+/*
+ * This file contains all generic CNID related stuff
  * declarations. Included:
- * - CNID factory, which retrieves (eventually instantiates) 
+ * - CNID factory, which retrieves (eventually instantiates)
  *   CNID objects on demand
  * - selection of CNID backends (default, detected by volume)
  * - full set of CNID operations needed by server core.
@@ -33,8 +32,8 @@
 #define CNID_FLAG_BLOCK        0x08      /* block signals in update. */
 #define CNID_FLAG_NODEV        0x10      /* don't use device number only inode */
 #define CNID_FLAG_LAZY_INIT    0x20      /* */
-#define CNID_FLAG_MEMORY       0x40     /* this is a memory only db */
-#define CNID_FLAG_INODE        0x80     /* in cnid_add the inode is authoritative */
+#define CNID_FLAG_MEMORY       0x40  /* this is a memory only db */
+#define CNID_FLAG_INODE        0x80  /* in cnid_add the inode is authoritative */
 
 #define CNID_INVALID   0
 /* first valid ID */
  * This is instance of CNID database object.
  */
 struct _cnid_db {
-       
-       u_int32_t flags;             /* Flags describing some CNID backend aspects. */
-       char *volpath;               /* Volume path this particular CNID db refers to. */
-       void *_private;              /* back-end speficic data */
-
-       cnid_t (*cnid_add)(struct _cnid_db *cdb, const struct stat *st, const cnid_t did, 
-                       char *name, const size_t, cnid_t hint);
-       int (*cnid_delete)(struct _cnid_db *cdb, cnid_t id);
-       cnid_t (*cnid_get)(struct _cnid_db *cdb, const cnid_t did, char *name, const  size_t);
-       cnid_t (*cnid_lookup)(struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
-                       char *name, const size_t);
-       cnid_t (*cnid_nextid)(struct _cnid_db *cdb);
-       char *(*cnid_resolve)(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len);
-       int (*cnid_update)(struct _cnid_db *cdb, const cnid_t id, const struct stat *st, 
-                       const cnid_t did, char *name, const size_t len);        
-       void (*cnid_close)(struct _cnid_db *cdb);
-       int  (*cnid_getstamp)(struct _cnid_db *cdb, void *buffer, const size_t len);
-        cnid_t (*cnid_rebuild_add)(struct _cnid_db *, const struct stat *, const cnid_t,
-                        char *, const size_t, cnid_t);
+    u_int32_t flags;             /* Flags describing some CNID backend aspects. */
+    char *volpath;               /* Volume path this particular CNID db refers to. */
+    void *_private;              /* back-end speficic data */
+
+    cnid_t (*cnid_add)         (struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
+                                char *name, const size_t, cnid_t hint);
+    int    (*cnid_delete)      (struct _cnid_db *cdb, cnid_t id);
+    cnid_t (*cnid_get)         (struct _cnid_db *cdb, const cnid_t did, char *name, const  size_t);
+    cnid_t (*cnid_lookup)      (struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
+                                char *name, const size_t);
+    cnid_t (*cnid_nextid)      (struct _cnid_db *cdb);
+    char * (*cnid_resolve)     (struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len);
+    int    (*cnid_update)      (struct _cnid_db *cdb, const cnid_t id, const struct stat *st,
+                                const cnid_t did, char *name, const size_t len);
+    void   (*cnid_close)       (struct _cnid_db *cdb);
+    int    (*cnid_getstamp)    (struct _cnid_db *cdb, void *buffer, const size_t len);
+    cnid_t (*cnid_rebuild_add) (struct _cnid_db *, const struct stat *, const cnid_t,
+                                char *, const size_t, cnid_t);
+    int    (*cnid_find)        (struct _cnid_db *cdb, char *name, size_t namelen,
+                                void *buffer, size_t buflen);
 };
 typedef struct _cnid_db cnid_db;
 
-/* 
+/*
  * Consolidation of args passedn from main cnid_open to modules cnid_XXX_open, so
  * that it's easier to add aditional args as required.
  */
@@ -88,10 +88,10 @@ struct cnid_open_args {
  * CNID module - represents particular CNID implementation
  */
 struct _cnid_module {
-       char *name;
-       struct list_head db_list;   /* CNID modules are also stored on a bidirectional list. */
-       struct _cnid_db *(*cnid_open)(struct cnid_open_args *args);
-       u_int32_t flags;            /* Flags describing some CNID backend aspects. */
+    char *name;
+    struct list_head db_list;   /* CNID modules are also stored on a bidirectional list. */
+    struct _cnid_db *(*cnid_open)(struct cnid_open_args *args);
+    u_int32_t flags;            /* Flags describing some CNID backend aspects. */
 
 };
 typedef struct _cnid_module cnid_module;
@@ -107,98 +107,22 @@ struct _cnid_db *cnid_open(const char *volpath,
                            mode_t mask,
                            char *type,
                            int flags,
-                           const char *cnidsrv, /* Only for dbd */
-                           const char *cnidport); /* Only for dbd */
-
-cnid_t cnid_add(struct _cnid_db *cdb, const struct stat *st, const cnid_t did, 
-                       char *name, const size_t len, cnid_t hint);
-
-int    cnid_delete(struct _cnid_db *cdb, cnid_t id);
-
-cnid_t cnid_get   (struct _cnid_db *cdb, const cnid_t did, char *name,const size_t len);
-
-int    cnid_getstamp(struct _cnid_db *cdb, void *buffer, const size_t len);
-
-cnid_t cnid_lookup(struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
-                       char *name, const size_t len);
-
-char *cnid_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len);
-
-int cnid_update   (struct _cnid_db *cdb, const cnid_t id, const struct stat *st, 
-                       const cnid_t did, char *name, const size_t len);
-
+                           const char *cnidsrv,
+                           const char *cnidport);
+cnid_t cnid_add        (struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
+                        const char *name, const size_t len, cnid_t hint);
+int    cnid_delete     (struct _cnid_db *cdb, cnid_t id);
+cnid_t cnid_get        (struct _cnid_db *cdb, const cnid_t did, char *name,const size_t len);
+int    cnid_getstamp   (struct _cnid_db *cdb, void *buffer, const size_t len);
+cnid_t cnid_lookup     (struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
+                        char *name, const size_t len);
+char  *cnid_resolve    (struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len);
+int    cnid_update     (struct _cnid_db *cdb, const cnid_t id, const struct stat *st,
+                        const cnid_t did, char *name, const size_t len);
 cnid_t cnid_rebuild_add(struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
                         char *name, const size_t len, cnid_t hint);
-
-
-/* This function closes a CNID database and frees all resources assigned to it. */ 
-void cnid_close(struct _cnid_db *db);
+int    cnid_find       (struct _cnid_db *cdb, const char *name, size_t namelen,
+                        void *buffer, size_t buflen);
+void   cnid_close      (struct _cnid_db *db);
 
 #endif
-
-/*
- * $Log: cnid.h,v $
- * Revision 1.15  2010-03-31 09:47:32  franklahm
- * clustering support: new per volume option cnidserver
- *
- * Revision 1.14  2009/11/28 13:09:25  didg
- * guard against confused DB returning junk values
- *
- * Revision 1.13  2009/11/24 12:18:19  didg
- * add a flag parameter to cnid open functions
- *
- * Revision 1.12  2005/09/07 15:23:21  didg
- *
- * lazy init dbd database, help with pre tiger OS and a lot of volumes.
- *
- * Revision 1.11  2005/05/03 14:55:12  didg
- *
- * remove gcc warning
- *
- * Revision 1.10  2005/04/28 20:49:51  bfernhomberg
- *
- * - merge branch-netatalk-afp-3x-dev, HEAD was tagged before
- *
- * Revision 1.9.6.8  2005/04/25 22:33:24  lenneis
- * Add a new interface to the cdb and dbd backends: cnid_rebuild_add. It
- * takes dev, ino, did, name and cnid and writes these values unconditionally
- * into the cnid database. To be used in a recovery tool that writes cnids
- * cached in AppleDouble files back into the database. Not used yet by
- * any daemons or command line utilities.
- *
- * Revision 1.9.6.7  2005/02/08 11:46:59  didg
- *
- * warnings fixes from 2.0 branch
- *
- * Revision 1.9.6.6  2004/02/22 18:36:37  didg
- *
- * small clean up
- *
- * Revision 1.9.6.5  2004/01/14 23:15:19  lenneis
- * Check if we can get a DB stamp sucessfully in afs_openvol and fail
- * the open if not.
- *
- * Revision 1.9.6.4  2004/01/10 07:19:31  bfernhomberg
- * add cnid_init prototype
- *
- * Revision 1.9.6.3  2004/01/03 22:42:55  didg
- *
- * better errors handling in afpd for dbd cnid.
- *
- * Revision 1.9.6.2  2004/01/03 22:21:09  didg
- *
- * add nodev volume option (always use 0 for device number).
- *
- * Revision 1.9.6.1  2003/09/09 16:42:20  didg
- *
- * big merge for db frontend and unicode.
- *
- * Revision 1.9.4.2  2003/06/11 15:29:11  rlewczuk
- * Removed obsolete parameter from cnid_add. Spotted by Didier.
- *
- * Revision 1.9.4.1  2003/05/29 07:53:19  rlewczuk
- * Selectable CNIDs. Some refactoring. Propably needs more of refactoring, mainly
- * a well designed API (current API is just an old cnid_* API enclosed in VMT).
- *
- */
-
index 4e4847a7eb4ab5bb84bd913036e4a784ad928569..36dee3d8264890402fa6cc7b0f7d233c08efa706 100644 (file)
 #define CNID_DBD_OP_MANGLE_GET  0x0a
 #define CNID_DBD_OP_GETSTAMP    0x0b
 #define CNID_DBD_OP_REBUILD_ADD 0x0c
+#define CNID_DBD_OP_SEARCH      0x0d
 
 #define CNID_DBD_RES_OK            0x00
 #define CNID_DBD_RES_NOTFOUND      0x01
 #define CNID_DBD_RES_ERR_DB        0x02
 #define CNID_DBD_RES_ERR_MAX       0x03
 #define CNID_DBD_RES_ERR_DUPLCNID  0x04
+#define CNID_DBD_RES_SRCH_CNT      0x05
+#define CNID_DBD_RES_SRCH_DONE     0x06
+
+#define DBD_MAX_SRCH_RSLTS 100
 
 struct cnid_dbd_rqst {
     int     op;
     cnid_t  cnid;
     dev_t   dev;
     ino_t   ino;
-    u_int32_t type;
+    uint32_t type;
     cnid_t  did;
-    char   *name;
+    char    *name;
     size_t  namelen;
 };
 
@@ -46,7 +51,7 @@ struct cnid_dbd_rply {
     int     result;    
     cnid_t  cnid;
     cnid_t  did;
-    char   *name;
+    char    *name;
     size_t  namelen;
 };
 
index 6525ce4898c63508bb6c8cb97979cfea61800fdd..f5f825faa362f806e124cf3e0e32280166756a9c 100644 (file)
 #define CNID_INO_LEN             8
  
 #define CNID_DEVINO_OFS          CNID_LEN
-#define CNID_DEVINO_LEN          (CNID_DEV_LEN +CNID_INO_LEN)
+#define CNID_DEVINO_LEN          (CNID_DEV_LEN + CNID_INO_LEN)
  
-#define CNID_TYPE_OFS            (CNID_DEVINO_OFS +CNID_DEVINO_LEN)
+#define CNID_TYPE_OFS            (CNID_DEVINO_OFS + CNID_DEVINO_LEN)
 #define CNID_TYPE_LEN            4
  
-#define CNID_DID_OFS             (CNID_TYPE_OFS +CNID_TYPE_LEN)
+#define CNID_DID_OFS             (CNID_TYPE_OFS + CNID_TYPE_LEN)
 #define CNID_DID_LEN             CNID_LEN
  
 #define CNID_NAME_OFS            (CNID_DID_OFS + CNID_DID_LEN)
 #define ROOTINFO_KEYLEN 4
 
 /* 
-   Rootinfo data:
-   4 unused bytes (cnid) 
-   8 bytes, in first 4 bytes db stamp: struct stat.st_ctime of database file (dev)
-   8 unused bytes (inode)
+   Rootinfo data, fields as they are used by normal entries for CNIDs (for reference):
+   4 bytes: CNID 
+   8 bytes: dev
+   8 bytes: inode
    4 bytes: is a file/directory (type)
-   4 unused bytes (did)
-   9 bytes name "RootInfo"
+   4 bytes: DID
+   x bytes: name
+
+   Contents in Rootinfo entry:
+   4 bytes: 0
+   8 bytes: db stamp: struct stat.st_ctime of database file
+   8 bytes: unused
+   4 bytes: last used CNID
+   4 bytes: version as htonl(uint32_t)
+   9 bytes: name "RootInfo"
 */
 #define ROOTINFO_DATA    "\0\0\0\0" \
                          "\0\0\0\0\0\0\0\0" \
                          "RootInfo"
 #define ROOTINFO_DATALEN (3*4 + 2*8 + 9)
 
+/* 
+ * CNID version history:
+ * 0: up to Netatalk 2.1.x
+ * 1: starting with 2.2, additional name index, used in cnid_find
+ */
+#define CNID_VERSION_0               0
+#define CNID_VERSION_1               1
+#define CNID_VERSION_UNINTIALIZED_DB UINT32_MAX
+
+/* Current CNID version */
+#define CNID_VERSION CNID_VERSION_1
+
 #endif
index 17e5d08762468e2555d2b2396a5baba3e052da93..d15c048940e772229334e10f5f50aad76eefb9da 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: directory.h,v 1.3 2010-01-10 10:58:25 franklahm Exp $
- *
  * Copyright (c) 1990,1991 Regents of The University of Michigan.
  * All Rights Reserved.
  *
 #include <sys/types.h>
 #include <netatalk/endian.h>
 #include <dirent.h>
+#include <stdint.h>
 
 #include <atalk/cnid.h>
-#include <atalk/directory.h>
+#include <atalk/bstrlib.h>
+#include <atalk/queue.h>
 
 /* setgid directories */
 #ifndef DIRBITS
 # endif /* AFS */
 #endif /* DIRBITS */
 
-/* the did tree is now a red-black tree while the parent/child
- * tree is a circular doubly-linked list. how exciting. */
-struct dir {
-    struct dir *d_left, *d_right, *d_back; /* for red-black tree */
-    int                d_color;
-    struct dir  *d_parent, *d_child; /* parent-child */
-    struct dir  *d_prev, *d_next;    /* siblings */
-    void        *d_ofork;            /* oforks using this directory. */
-    u_int32_t   d_did;
-    int                d_flags;
-
-    time_t      ctime;                /* inode ctime */
-    u_int32_t   offcnt;               /* offspring count */
+/* reserved directory id's */
+#define DIRDID_ROOT_PARENT    htonl(1)  /* parent directory of root */
+#define DIRDID_ROOT           htonl(2)  /* root directory */
 
-    char       *d_m_name;            /* mac name */
-    char        *d_u_name;            /* unix name */
-    ucs2_t     *d_m_name_ucs2;       /* mac name as UCS2 */
+struct dir {
+    bstring     d_fullpath;          /* complete unix path to dir */
+    bstring     d_m_name;            /* mac name */
+    bstring     d_u_name;            /* unix name                                          */
+                                     /* be careful here! if d_m_name == d_u_name, d_u_name */
+                                     /* will just point to the same storage as d_m_name !! */
+    ucs2_t      *d_m_name_ucs2;       /* mac name as UCS2 */
+    qnode_t     *qidx_node;           /* pointer to position in queue index */
+    time_t      ctime;                /* inode ctime, used and modified by reenumeration */
+    time_t      ctime_dircache;       /* inode ctime, used and modified by dircache */
+    int         d_flags;              /* directory flags */
+    cnid_t      d_pdid;               /* CNID of parent directory */
+    cnid_t      d_did;                /* CNID of directory */
+    uint32_t    offcnt;               /* offspring count */
+    uint16_t    d_vid;                /* only needed in the dircache, because
+                                         we put all directories in one cache. */
 };
 
 struct path {
     int         m_type;             /* mac name type (long name, unicode */
-    char       *m_name;            /* mac name */
+    char        *m_name;            /* mac name */
     char        *u_name;            /* unix name */
-    cnid_t     id;                 /* file id (only for getmetadata) */
+    cnid_t      id;                 /* file id (only for getmetadata) */
     struct dir  *d_dir;             /* */
     int         st_valid;           /* does st_errno and st set */
     int         st_errno;
@@ -83,5 +86,4 @@ static inline int path_isadir(struct path *o_path)
 #endif
 }
 
-
 #endif /* ATALK_DIRECTORY_H */
index 82da7a1595e517b0b0eea4ad761b4cb68d9dbd43..babd8288ce3fc55745b5acb7d2897479822ac4f5 100644 (file)
@@ -55,18 +55,17 @@ struct dsi_block {
 
 #define DSI_CMDSIZ        8192 
 #define DSI_DATASIZ       8192
+
 /* child and parent processes might interpret a couple of these
  * differently. */
 typedef struct DSI {
   dsi_proto protocol;
   struct dsi_block header;
   struct sockaddr_storage server, client;
-  
   struct itimerval timer;
-
-  int     in_write;      /* in the middle of writing multiple packets, signal handlers
-                          * can't write to the socket 
-                         */
+  int      tickle;        /* tickle count */
+  int     in_write;      /* in the middle of writing multiple packets,
+                             signal handlers can't write to the socket */
   int      msg_request;   /* pending message to the client */
   int      down_request;  /* pending SIGUSR1 down in 5 mn */
 
@@ -77,9 +76,7 @@ typedef struct DSI {
   size_t statuslen;
   size_t datalen, cmdlen;
   off_t  read_count, write_count;
-  int asleep; /* client won't reply AFP 0x7a ? */
-  /* inited = initialized?, child = a child?, noreply = send reply? */
-  char child, inited, noreply;
+  uint32_t flags;             /* DSI flags like DSI_SLEEPING, DSI_DISCONNECTED */
   const char *program; 
   int socket, serversock;
 
@@ -94,13 +91,17 @@ typedef struct DSI {
   char srvloc_url[512];
 #endif 
 
-  /* buffer for OSX deadlock */
+#ifdef USE_ZEROCONF
+  char *bonjourname;      /* server name as UTF8 maxlen MAXINSTANCENAMELEN */
+  int zeroconf_registered;
+#endif
+
+  /* DSI readahead buffer used for buffered reads in dsi_peek */
+  size_t dsireadbuf; /* size of the DSI readahead buffer used in dsi_peek() */
   char *buffer;
   char *start;
   char *eof;
   char *end;
-  int  maxsize;
-
 } DSI;
   
 /* DSI flags */
@@ -111,6 +112,7 @@ typedef struct DSI {
 /* DSI session options */
 #define DSIOPT_SERVQUANT 0x00   /* server request quantum */
 #define DSIOPT_ATTNQUANT 0x01   /* attention quantum */
+#define DSIOPT_REPLCSIZE 0x02   /* AFP replaycache size supported by the server (that's us) */
 
 /* DSI Commands */
 #define DSIFUNC_CLOSE   1       /* DSICloseSession */
@@ -144,6 +146,18 @@ typedef struct DSI {
 /* default port number */
 #define DSI_AFPOVERTCP_PORT 548
 
+/* DSI session State flags */
+#define DSI_DATA             (1 << 0) /* we have received a DSI command */
+#define DSI_RUNNING          (1 << 1) /* we have received a AFP command */
+#define DSI_SLEEPING         (1 << 2) /* we're sleeping after FPZzz */
+#define DSI_EXTSLEEP         (1 << 3) /* we're sleeping after FPZzz */
+#define DSI_DISCONNECTED     (1 << 4) /* we're in diconnected state after a socket error */
+#define DSI_DIE              (1 << 5) /* SIGUSR1, going down in 5 minutes */
+#define DSI_NOREPLY          (1 << 6) /* in dsi_write we generate our own replies */
+#define DSI_RECONSOCKET      (1 << 7) /* we have a new socket from primary reconnect */
+#define DSI_RECONINPROG      (1 << 8) /* used in the new session in reconnect */
+#define DSI_AFP_LOGGED_OUT   (1 << 9) /* client called afp_logout, quit on next EOF from socket */
+
 /* basic initialization: dsi_init.c */
 extern DSI *dsi_init (const dsi_proto /*protocol*/,
                          const char * /*program*/, 
@@ -153,7 +167,7 @@ extern DSI *dsi_init (const dsi_proto /*protocol*/,
 extern void dsi_setstatus (DSI *, char *, const size_t);
 
 /* in dsi_getsess.c */
-extern DSI *dsi_getsession (DSI *, server_child *, const int);
+extern afp_child_t *dsi_getsession (DSI *, server_child *, const int);
 extern void dsi_kill (int);
 
 
@@ -164,7 +178,6 @@ extern int  dsi_cmdreply (DSI *, const int);
 extern int dsi_tickle (DSI *);
 extern void dsi_getstatus (DSI *);
 extern void dsi_close (DSI *);
-extern void dsi_sleep (DSI *, const int );
 
 #define DSI_NOWAIT 1
 /* low-level stream commands -- in dsi_stream.c */
index 00d987de77b27bd37fcc69d3949e65416af386b9..c0165a1a257f210999055d8a271bc81dadf9e2a8 100644 (file)
@@ -20,7 +20,7 @@
 #include <config.h>
 #endif
 
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_SOLARIS_ACLS
 #include <sys/acl.h>
 #endif
 
diff --git a/include/atalk/errchk.h b/include/atalk/errchk.h
new file mode 100644 (file)
index 0000000..64a1276
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+   Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+   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.
+ */
+
+#ifndef ERRCHECK_H
+#define ERRCHECK_H
+
+#define EC_INIT int ret = 0
+#define EC_STATUS(a) ret = (a)
+#define EC_FAIL ret = -1; goto cleanup
+#define EC_CLEANUP cleanup
+#define EC_EXIT return ret
+
+/* 
+ * Check out doc/DEVELOPER for more infos.
+ *
+ * We have these macros:
+ * EC_ZERO, EC_ZERO_LOG, EC_ZERO_LOGSTR, EC_ZERO_LOG_ERR, EC_ZERO_CUSTOM
+ * EC_NEG1, EC_NEG1_LOG, EC_NEG1_LOGSTR, EC_NEG1_LOG_ERR, EC_NEG1_CUSTOM
+ * EC_NULL, EC_NULL_LOG, EC_NULL_LOGSTR, EC_NULL_LOG_ERR, EC_NULL_CUSTOM
+ *
+ * A boileplate function template is:
+
+   int func(void)
+   {
+       EC_INIT;
+
+       ...your code here...
+
+       EC_STATUS(0);
+
+   EC_CLEANUP:
+       EC_EXIT;
+   }
+ */
+
+/* check for return val 0 which is ok, every other is an error, prints errno */
+#define EC_ZERO_LOG(a)                                                  \
+    do {                                                                \
+        if ((a) != 0) {                                                 \
+            LOG(log_error, logtype_default, "%s failed: %s", #a, strerror(errno)); \
+            ret = -1;                                                   \
+            goto cleanup;                                               \
+        }                                                               \
+    } while (0)
+
+#define EC_ZERO_LOGSTR(a, b, ...)                                       \
+    do {                                                                \
+        if ((a) != 0) {                                                 \
+            LOG(log_error, logtype_default, b, __VA_ARGS__);            \
+            ret = -1;                                                   \
+            goto cleanup;                                               \
+        }                                                               \
+    } while (0)
+
+#define EC_ZERO_LOG_ERR(a, b)                                           \
+    do {                                                                \
+        if ((a) != 0) {                                                 \
+            LOG(log_error, logtype_default, "%s failed: %s", #a, strerror(errno)); \
+            ret = (b);                                                  \
+            goto cleanup;                                               \
+        }                                                               \
+    } while (0)
+
+#define EC_ZERO(a)                              \
+    do {                                        \
+        if ((a) != 0) {                         \
+            ret = -1;                           \
+            goto cleanup;                       \
+        }                                       \
+    } while (0)
+
+#define EC_ZERO_ERR(a,b )                       \
+    do {                                        \
+        if ((a) != 0) {                         \
+            ret = b;                            \
+            goto cleanup;                       \
+        }                                       \
+    } while (0)
+
+/* check for return val 0 which is ok, every other is an error, prints errno */
+#define EC_NEG1_LOG(a)                                                  \
+    do {                                                                \
+        if ((a) == -1) {                                                \
+            LOG(log_error, logtype_default, "%s failed: %s", #a, strerror(errno)); \
+            ret = -1;                                                   \
+            goto cleanup;                                               \
+        }                                                               \
+    } while (0)
+
+#define EC_NEG1_LOGSTR(a, b, ...)                               \
+    do {                                                        \
+        if ((a) == -1) {                                        \
+            LOG(log_error, logtype_default, b, __VA_ARGS__);    \
+            ret = -1;                                           \
+            goto cleanup;                                       \
+        }                                                       \
+    } while (0)
+
+#define EC_NEG1_LOG_ERR(a, b)                                           \
+    do {                                                                \
+        if ((a) == -1) {                                                \
+            LOG(log_error, logtype_default, "%s failed: %s", #a, strerror(errno)); \
+            ret = b;                                                    \
+            goto cleanup;                                               \
+        }                                                               \
+    } while (0)
+
+#define EC_NEG1(a)                              \
+    do {                                        \
+        if ((a) == -1) {                        \
+            ret = -1;                           \
+            goto cleanup;                       \
+        }                                       \
+    } while (0)
+
+/* check for return val != NULL, prints errno */
+#define EC_NULL_LOG(a)                                                  \
+    do {                                                                \
+        if ((a) == NULL) {                                              \
+            LOG(log_error, logtype_default, "%s failed: %s", #a, strerror(errno)); \
+            ret = -1;                                                   \
+            goto cleanup;                                               \
+        }                                                               \
+    } while (0)
+
+#define EC_NULL_LOGSTR(a, b, ...)                                       \
+    do {                                                                \
+        if ((a) == NULL) {                                              \
+            LOG(log_error, logtype_default, b , __VA_ARGS__);           \
+            ret = -1;                                                   \
+            goto cleanup;                                               \
+        } \
+    } while (0)
+
+#define EC_NULL_LOG_ERR(a, b)                                           \
+    do {                                                                \
+        if ((a) == NULL) {                                              \
+            LOG(log_error, logtype_default, "%s failed: %s", #a, strerror(errno)); \
+            ret = b;                                                    \
+            goto cleanup;                                               \
+        }                                                               \
+    } while (0)
+
+#define EC_NULL(a)                              \
+    do {                                        \
+        if ((a) == NULL) {                      \
+            ret = -1;                           \
+            goto cleanup;                       \
+        }                                       \
+    } while (0)
+
+#endif /* ERRCHECK_H */
diff --git a/include/atalk/ftw.h b/include/atalk/ftw.h
new file mode 100644 (file)
index 0000000..157efc5
--- /dev/null
@@ -0,0 +1,109 @@
+/* Copyright (C) 1992,1996-1999,2003,2004 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+/*
+ *     X/Open Portability Guide 4.2: ftw.h
+ */
+
+#ifndef _ATALK_FTW_H
+#define        _ATALK_FTW_H    1
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/* Values for the FLAG argument to the user function passed to `ftw'
+   and 'nftw'.  */
+enum
+{
+  FTW_F,               /* Regular file.  */
+#define FTW_F   FTW_F
+  FTW_D,               /* Directory.  */
+#define FTW_D   FTW_D
+  FTW_DNR,             /* Unreadable directory.  */
+#define FTW_DNR         FTW_DNR
+  FTW_NS,              /* Unstatable file.  */
+#define FTW_NS  FTW_NS
+  FTW_SL,              /* Symbolic link.  */
+# define FTW_SL         FTW_SL
+
+/* These flags are only passed from the `nftw' function.  */
+  FTW_DP,              /* Directory, all subdirs have been visited. */
+# define FTW_DP         FTW_DP
+  FTW_SLN              /* Symbolic link naming non-existing file.  */
+# define FTW_SLN FTW_SLN
+};
+
+
+/* Flags for fourth argument of `nftw'.  */
+enum
+{
+  FTW_PHYS = 1,                /* Perform physical walk, ignore symlinks.  */
+# define FTW_PHYS      FTW_PHYS
+  FTW_MOUNT = 2,       /* Report only files on same file system as the
+                          argument.  */
+# define FTW_MOUNT     FTW_MOUNT
+  FTW_CHDIR = 4,       /* Change to current directory while processing it.  */
+# define FTW_CHDIR     FTW_CHDIR
+  FTW_DEPTH = 8,       /* Report files in directory before directory itself.*/
+# define FTW_DEPTH     FTW_DEPTH
+  FTW_ACTIONRETVAL = 16        /* Assume callback to return FTW_* values instead of
+                          zero to continue and non-zero to terminate.  */
+#  define FTW_ACTIONRETVAL FTW_ACTIONRETVAL
+};
+
+/* Return values from callback functions.  */
+enum
+{
+  FTW_CONTINUE = 0,    /* Continue with next sibling or for FTW_D with the
+                          first child.  */
+# define FTW_CONTINUE  FTW_CONTINUE
+  FTW_STOP = 1,                /* Return from `ftw' or `nftw' with FTW_STOP as return
+                          value.  */
+# define FTW_STOP      FTW_STOP
+  FTW_SKIP_SUBTREE = 2,        /* Only meaningful for FTW_D: Don't walk through the
+                          subtree, instead just continue with its next
+                          sibling. */
+# define FTW_SKIP_SUBTREE FTW_SKIP_SUBTREE
+  FTW_SKIP_SIBLINGS = 3,/* Continue with FTW_DP callback for current directory
+                           (if FTW_DEPTH) and then its siblings.  */
+# define FTW_SKIP_SIBLINGS FTW_SKIP_SIBLINGS
+};
+
+/* Structure used for fourth argument to callback function for `nftw'.  */
+struct FTW
+  {
+    int base;
+    int level;
+  };
+
+/* Convenient types for callback functions.  */
+typedef int (*nftw_func_t) (const char *filename,
+                            const struct stat *status,
+                            int flag,
+                            struct FTW *info);
+#define NFTW_FUNC_T nftw_func_t
+
+typedef void (*dir_notification_func_t) (void);
+
+extern int nftw(const char *dir,
+                nftw_func_t func,
+                dir_notification_func_t up,
+                int descriptors,
+                int flag);
+
+#endif /* ATALK_FTW_H */
index 2fb2ca292113c784141cf3a8f31d0169f7356852..70f9f628c7c61c0c0fe7018b91bfceb884865f43 100644 (file)
@@ -1,4 +1,4 @@
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_ACLS
 
 #ifndef LDAPCONFIG_H
 #define LDAPCONFIG_H
@@ -37,6 +37,6 @@ extern struct ldap_pref ldap_prefs[];
 extern struct pref_array prefs_array[];
 extern int ldap_config_valid;
 
-#endif
+#endif /* LDAPCONFIG_H */
 
-#endif
+#endif /* HAVE_ACLS */
index 2d7d91bdff7e1a22649f0cf845076761df3f634c..2e75b461b53c0ab198b2a8f44aecf08057c32ea3 100644 (file)
 #  define _PATH_AFPDLOCK       ATALKPATHCAT(_PATH_LOCKDIR,"afpd")
 #endif
 
+/*
+ * cnid_metad paths
+ */
+#if defined (FHS_COMPATIBILITY) || defined (__NetBSD__)
+#  define _PATH_CNID_METAD_LOCK        ATALKPATHCAT(_PATH_LOCKDIR,"cnid_metad.pid")
+#else
+#  define _PATH_CNID_METAD_LOCK        ATALKPATHCAT(_PATH_LOCKDIR,"cnid_metad")
+#endif
+
 #endif /* atalk/paths.h */
diff --git a/include/atalk/queue.h b/include/atalk/queue.h
new file mode 100644 (file)
index 0000000..a3f433e
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+   Copyright (c) 2010 Frank Lahm
+   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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef ATALK_QUEUE_H
+#define ATALK_QUEUE_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+typedef struct qnode {
+    struct qnode *prev;
+    struct qnode *next;
+    void *data;
+} qnode_t;
+
+typedef qnode_t q_t;
+
+extern q_t *queue_init(void);
+extern void queue_destroy(q_t *q, void (*callback)(void *));
+#define queue_free(q) queue_destroy((q), free)
+extern qnode_t *enqueue(q_t *q, void *data);
+extern qnode_t *prequeue(q_t *q, void *data);
+extern void *dequeue(q_t *q);
+
+#endif  /* ATALK_QUEUE_H */
index 8479367bb4f42b2c8786d0ce4a48145aef07b659..b45a3a42daf088c958618a69a6f350c4ff6653a4 100644 (file)
@@ -23,17 +23,30 @@ typedef struct server_child {
   int count, nsessions, nforks;
 } server_child;
 
+typedef struct server_child_data {
+  pid_t     pid;               /* afpd worker process pid (from the worker afpd process )*/
+  uid_t     uid;               /* user id of connected client (from the worker afpd process) */
+  int       valid;             /* 1 if we have a clientid */
+  uint32_t  time;              /* client boot time (from the mac client) */
+  int       killed;            /* 1 if we already tried to kill the client */
+  uint32_t  idlen;             /* clientid len (from the Mac client) */
+  char      *clientid;  /* clientid (from the Mac client) */
+  int       ipc_fds[2]; /* socketpair for IPC bw */
+  struct server_child_data **prevp, *next;
+} afp_child_t;
+
+extern int parent_or_child;
+
 /* server_child.c */
 extern server_child *server_child_alloc (const int, const int);
-extern int server_child_add (server_child *, const int, const pid_t);
-extern int server_child_remove (server_child *, const int, const pid_t);
+extern afp_child_t *server_child_add (server_child *, int, pid_t, uint ipc_fds[2]);
+extern int  server_child_remove (server_child *, const int, const pid_t);
 extern void server_child_free (server_child *);
 
 extern void server_child_kill (server_child *, const int, const int);
-extern void server_child_kill_one (server_child *children, const int forkid, const pid_t, const uid_t);
 extern void server_child_kill_one_by_id (server_child *children, const int forkid, const pid_t pid, const uid_t,
                                                const u_int32_t len, char *id, u_int32_t boottime);
-
+extern int  server_child_transfer_session(server_child *children, int forkid, pid_t, uid_t, int, uint16_t);
 extern void server_child_setup (server_child *, const int, void (*)(const pid_t));
 extern void server_child_handler (server_child *);
 extern void server_reset_signal (void);
index eb899f8c891564cb7fe2a509c9a0531eba6d8475..332897a0993bd2f34ca3be79f942bfd75fe27818 100644 (file)
@@ -1,16 +1,12 @@
+#ifndef ATALK_SERVER_IPC_H
+#define ATALK_SERVER_IPC_H
 
 #include <atalk/server_child.h>
 
-#define IPC_KILLTOKEN   1
-#define IPC_GETSESSION  2
-
-void *server_ipc_create(void);
-int server_ipc_child(void *obj);
-int server_ipc_parent(void *obj);
-int server_ipc_read(server_child *children);
-int server_ipc_write(u_int16_t command, int len, void *token);
-
-
-
+#define IPC_DISCOLDSESSION   0
+#define IPC_GETSESSION       1
 
+int ipc_server_read(server_child *children, int fd);
+int ipc_child_write(int fd, uint16_t command, int len, void *token);
 
+#endif /* IPC_GETSESSION_LOGIN */
index 1cea56c4d13347481fc687ac379a39d649c4ffaf..325b7ac2bf911275095a0939ce21109f33a953b1 100644 (file)
@@ -92,9 +92,13 @@ extern int atalk_iconv_close (atalk_iconv_t);
 extern struct charset_functions *find_charset_functions (const char *);
 extern int atalk_register_charset (struct charset_functions *);
 
+/* from utf16_case.c */
+extern ucs2_t    toupper_w  (ucs2_t);
+extern u_int32_t toupper_sp (u_int32_t);
+extern ucs2_t    tolower_w  (ucs2_t);
+extern u_int32_t tolower_sp (u_int32_t);
+
 /* from util_unistr.c */
-extern ucs2_t   toupper_w  (ucs2_t);
-extern ucs2_t   tolower_w  (ucs2_t);
 extern int      strupper_w (ucs2_t *);
 extern int      strlower_w (ucs2_t *);
 extern int      islower_w  (ucs2_t);
index 0d1f59ae8f1c502811bc4db2edd02ab4a9d2af18..999f14daac86349e508be888df10e0a40a987123 100644 (file)
@@ -1,7 +1,3 @@
-/*
- * $Id: util.h,v 1.21 2010-02-28 22:29:16 didg Exp $
- */
-
 /*!
  * @file
  * Netatalk utility functions
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif /* HAVE_UNISTD_H */
+#include <poll.h>
 #include <netatalk/at.h>
+
 #include <atalk/unicode.h>
+#include <atalk/bstrlib.h>
 
 /* exit error codes */
 #define EXITERR_CLNT 1  /* client related error */
 #define EXITERR_CONF 2  /* error in config files/cmd line parameters */
 #define EXITERR_SYS  3  /* local system error */
 
+/* Print a SBT and exit */
+#define AFP_PANIC(why) \
+    do {                                            \
+        netatalk_panic(why);                        \
+        abort();                                    \
+    } while(0);
+
+/* LOG assert errors */
+#ifndef NDEBUG
+#define AFP_ASSERT(b) \
+    do {                                                                \
+        if (!(b)) {                                                     \
+            AFP_PANIC(#b);                                              \
+        } \
+    } while(0);
+#else
+#define AFP_ASSERT(b)
+#endif /* NDEBUG */
+
+#define STRCMP(a,b,c) (strcmp(a,c) b 0)
 
 #ifdef WITH_SENDFILE
 extern ssize_t sys_sendfile (int __out_fd, int __in_fd, off_t *__offset,size_t __count);
@@ -44,9 +63,9 @@ extern int atalk_aton     (char *, struct at_addr *);
 extern void bprint        (char *, int);
 extern int strdiacasecmp  (const char *, const char *);
 extern int strndiacasecmp (const char *, const char *, size_t);
-extern pid_t server_lock  (char * /*program*/, char * /*file*/, 
-                              int /*debug*/);
+extern pid_t server_lock  (char * /*program*/, char * /*file*/, int /*debug*/);
 extern void fault_setup          (void (*fn)(void *));
+extern void netatalk_panic(const char *why);
 #define server_unlock(x)  (unlink(x))
 
 /* strlcpy and strlcat are used by pam modules */
@@ -111,16 +130,48 @@ extern int lock_reg(int fd, int cmd, int type, off_t offest, int whence, off_t l
  ******************************************************************/
 
 extern int setnonblock(int fd, int cmd);
+extern ssize_t readt(int socket, void *data, const size_t length, int setnonblocking, int timeout);
+extern ssize_t writet(int socket, void *data, const size_t length, int setnonblocking, int timeout);
 extern const char *getip_string(const struct sockaddr *sa);
 extern unsigned int getip_port(const struct sockaddr *sa);
 extern void apply_ip_mask(struct sockaddr *ai, int maskbits);
 extern int compare_ip(const struct sockaddr *sa1, const struct sockaddr *sa2);
 
+/* Structures and functions dealing with dynamic pollfd arrays */
+enum fdtype {IPC_FD, LISTEN_FD};
+struct polldata {
+    enum fdtype fdtype; /* IPC fd or listening socket fd                 */
+    void *data;         /* pointer to AFPconfig for listening socket and *
+                         * pointer to afp_child_t for IPC fd             */
+};
+
+extern void fdset_add_fd(struct pollfd **fdsetp,
+                         struct polldata **polldatap,
+                         int *fdset_usedp,
+                         int *fdset_sizep,
+                         int fd,
+                         enum fdtype fdtype,
+                         void *data);
+extern void fdset_del_fd(struct pollfd **fdsetp,
+                         struct polldata **polldatap,
+                         int *fdset_usedp,
+                         int *fdset_sizep,
+                         int fd);
+extern int send_fd(int socket, int fd);
+extern int recv_fd(int fd, int nonblocking);
+
 /******************************************************************
  * unix.c
  *****************************************************************/
 
 extern const char *getcwdpath(void);
+extern char *stripped_slashes_basename(char *p);
 extern int lchdir(const char *dir);
-
+extern void randombytes(void *buf, int n);
 #endif  /* _ATALK_UTIL_H */
+
+/******************************************************************
+ * cnid.c
+ *****************************************************************/
+
+extern bstring rel_path_in_vol(const char *path, const char *volpath);
index 30c14ebfa269e402d3fa25e1000316783096eb09..a9432675af5dd2dac68dd0d5352676a6a0d9213e 100644 (file)
@@ -1,5 +1,4 @@
 /*
-   $Id: uuid.h,v 1.1 2009-02-02 11:55:01 franklahm Exp $
    Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
 
    This program is free software; you can redistribute it and/or modify
 #define UUID_BINSIZE 16
 #define UUID_STRINGSIZE 36
 
-typedef char *uuidp_t;
-typedef char uuid_t[UUID_BINSIZE];
+typedef unsigned char *uuidp_t;
+typedef unsigned char atalk_uuid_t[UUID_BINSIZE];
 
-typedef enum {UUID_USER = 1, UUID_GROUP} uuidtype_t;
+typedef enum {UUID_USER = 1, UUID_GROUP, UUID_LOCAL} uuidtype_t;
 extern char *uuidtype[];
 
 /* afp_options.c needs these. defined in libatalk/ldap.c */
@@ -41,33 +40,11 @@ extern char *ldap_uid_attr;
  * Interface
  ********************************************************/
 
-/*
- *   name: give me his name
- *   type: and type (UUID_USER or UUID_GROUP)
- *   uuid: and I'll try to return you his uuid
- * returns 0 on success !=0 on errror
- */  
 extern int getuuidfromname( const char *name, uuidtype_t type, uuidp_t uuid);
+extern int getnamefromuuid( const uuidp_t uuidp, char **name, uuidtype_t *type);
 
-/* 
- *   uuidp: give me a pointer to a uuid
- *   name: and I'll allocate a buf with his name and store a pointer to buf
- *   type: returns USER or GROUP
- * return 0 on success !=0 on errror
- */
-extern int getnamefromuuid( uuidp_t uuidp, char **name, uuidtype_t *type);
-
-/* 
- * convert 16 byte binary uuid to neat ascii represantation including dashes
- * string is allocated and pointer returned. caller must freee.
- */
-extern int uuid_bin2string( uuidp_t uuidp, char **uuidstring);
-
-
-/* 
- * convert ascii string that can include dashes to binary uuid.
- * caller must provide a buffer.
- */
+extern void localuuid_from_id(unsigned char *buf, uuidtype_t type, unsigned int id);
+extern const char *uuid_bin2string(unsigned char *uuid);
 extern void uuid_string2bin( const char *uuidstring, uuidp_t uuid);
 
 #endif /* AFP_UUID_H */
index de1c008ce22d2f1b50e82d752cab64d195896fdf..0bd5079655c56c0dea583c537bfd1d3da1247070 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <atalk/adouble.h>
 #include <atalk/volume.h>
+#include <atalk/acl.h>
 
 #define VFS_FUNC_ARGS_VALIDUPATH const struct vol *vol, const char *name
 #define VFS_FUNC_VARS_VALIDUPATH vol, name
 #define VFS_FUNC_ARGS_COPYFILE const struct vol *vol, int sfd, const char *src, const char *dst
 #define VFS_FUNC_VARS_COPYFILE vol, sfd, src, dst
 
+#ifdef HAVE_SOLARIS_ACLS
 #define VFS_FUNC_ARGS_ACL const struct vol *vol, const char *path, int cmd, int count, void *aces
 #define VFS_FUNC_VARS_ACL vol, path, cmd, count, aces
+#endif
+#ifdef HAVE_POSIX_ACLS
+#define VFS_FUNC_ARGS_ACL const struct vol *vol, const char *path, acl_type_t type, int count, acl_t acl
+#define VFS_FUNC_VARS_ACL vol, path, type, count, acl
+#endif
 
 #define VFS_FUNC_ARGS_REMOVE_ACL const struct vol *vol, const char *path, int dir
 #define VFS_FUNC_VARS_REMOVE_ACL vol, path, dir
@@ -101,9 +108,11 @@ struct vfs_ops {
     int (*vfs_renamefile)    (VFS_FUNC_ARGS_RENAMEFILE);
     int (*vfs_copyfile)      (VFS_FUNC_ARGS_COPYFILE);
 
+#ifdef HAVE_ACLS
     /* ACLs */
     int (*vfs_acl)           (VFS_FUNC_ARGS_ACL);
     int (*vfs_remove_acl)    (VFS_FUNC_ARGS_REMOVE_ACL);
+#endif
 
     /* Extended Attributes */
     int (*vfs_ea_getsize)    (VFS_FUNC_ARGS_EA_GETSIZE);
index 410b6b2aa0ddb10c1b22dfc25b2f96d46ceb7573..0f3a4024a6a36faedc4993783a731369e56237b3 100644 (file)
@@ -1,7 +1,3 @@
-/*
- * $Id: volinfo.h,v 1.10 2009-12-03 12:47:37 franklahm Exp $
- */
-
 #ifndef _ATALK_VOLINFO_H
 #define _ATALK_VOLINFO_H 1
 
@@ -18,6 +14,9 @@ typedef struct {
 } vol_opt_name_t;
 
 struct volinfo {
+    int                 retaincount;
+    int                 malloced;
+
     char                *v_name;
     char                *v_path;
     int                 v_flags;
@@ -33,10 +32,13 @@ struct volinfo {
     int                 v_vfs_ea;
     char                *(*ad_path)(const char *, int);
     char                *v_dbd_host;
-    int                 v_dbd_port;
+    char                *v_dbd_port;
 };
 
+struct volinfo *allocvolinfo(char *path);
 extern int loadvolinfo(char *path, struct volinfo *vol);
+extern void retainvolinfo(struct volinfo *vol);
+extern int closevolinfo(struct volinfo *vol);
 extern int savevolinfo(const struct vol *vol, const char *Cnid_srv, const char *Cnid_port);
 extern int vol_load_charsets(struct volinfo *vol);
 
index f1b2084b0c9fb6ebf47e01103fc898d20f110741..c2db33db7ccc6d1a4a6268e7a64f5cfdab12e0ad 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: volume.h,v 1.17 2010-04-10 08:24:54 franklahm Exp $
+ * $Id: volume.h,v 1.16 2010/03/31 09:47:32 franklahm Exp $
  *
  * Copyright (c) 1990,1994 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -27,9 +27,7 @@ struct vol {
     u_int16_t       v_vid;
     int             v_flags;
     char            *v_path;
-    struct dir      *v_dir, *v_root;
-    struct dir      *v_curdir;  /* cache */
-    hash_t          *v_hash;
+    struct dir      *v_root;
     time_t          v_mtime;
 
     charset_t       v_volcharset;
@@ -87,7 +85,7 @@ struct vol {
     char            *v_postexec;
     int             v_root_preexec_close;
     int             v_preexec_close;
-
+    char            *v_uuid;    /* For TimeMachine zeroconf record */
 #ifdef FORCE_UIDGID
     char            *v_forceuid;
     char            *v_forcegid;
@@ -108,7 +106,7 @@ struct vol {
 
 /*
   Flags that alter volume behaviour.
-  Keep in sync with include/atalk/volinfo.h and libatalk/util/volinfo.c
+  Keep in sync with libatalk/util/volinfo.c
 */
 #define AFPVOL_A2VOL     (1 << 5)   /* prodos volume */
 #define AFPVOL_CRLF      (1 << 6)   /* cr/lf translation */
@@ -135,8 +133,13 @@ struct vol {
 
 #define AFPVOL_CACHE     (1 << 21)   /* Use adouble v2 CNID caching. Default: yes */
 #define AFPVOL_INV_DOTS  (1 << 22)   /* dots files are invisible */
-#define AFPVOL_TM        (1 << 24)   /* Supports TimeMachine */
-#define AFPVOL_ACLS      (1 << 25)   /* Volume supports ACLS */
+#define AFPVOL_TM        (1 << 23)   /* Supports TimeMachine */
+#define AFPVOL_ACLS      (1 << 24)   /* Volume supports ACLS */
+#define AFPVOL_SEARCHDB  (1 << 25)   /* Use fast CNID db search instead of filesystem */
+/* Found this in branch dir-rewrite, maybe we want to use it sometimes */
+#if 0
+#define AFPVOL_CDROM     (1 << XX)   /* Ejectable media eg CD -> in memory CNID db */
+#endif
 
 /* Extended Attributes vfs indirection  */
 #define AFPVOL_EA_NONE           0   /* No EAs */
@@ -194,11 +197,7 @@ int wincheck(const struct vol *vol, const char *path);
 #define VOLPBIT_XBTOTAL 10
 #define VOLPBIT_BSIZE   11        /* block size */
 
-#ifdef AFP3x
 #define utf8_encoding() (afp_version >= 30)
-#else
-#define utf8_encoding() (0)
-#endif
 
 #define vol_noadouble(vol) (((vol)->v_flags & AFPVOL_NOADOUBLE) ? 1 : 0)
 #define vol_nodev(vol) (((vol)->v_flags & AFPVOL_NODEV) ? 1 : 0)
index 5fcf448399e1064f000a9860cfa09d8f59281bba..4c7500d6dd033db505b49ddc9114e2c403a55473 100644 (file)
@@ -1,16 +1,18 @@
 
 # Makefile.am for libatalk/
 
-SUBDIRS = acl adouble asp atp compat cnid dsi nbp netddp tdb util unicode vfs
+SUBDIRS = acl adouble asp atp bstring compat cnid dsi nbp netddp tdb util unicode vfs
 
 lib_LTLIBRARIES = libatalk.la
 
 libatalk_la_SOURCES = dummy.c
 
 libatalk_la_LIBADD  = \
+       acl/libacl.la \
        adouble/libadouble.la   \
        asp/libasp.la           \
        atp/libatp.la           \
+       bstring/libbstring.la \
        compat/libcompat.la     \
        dsi/libdsi.la           \
        nbp/libnbp.la           \
@@ -18,12 +20,14 @@ libatalk_la_LIBADD  = \
        util/libutil.la         \
        tdb/libtdb.la       \
        unicode/libunicode.la \
-       vfs/libvfs.la @LIBATALK_ACLS@
+       vfs/libvfs.la
 
 libatalk_la_DEPENDENCIES = \
+       acl/libacl.la \
        adouble/libadouble.la   \
        asp/libasp.la           \
        atp/libatp.la           \
+       bstring/libbstring.la \
        compat/libcompat.la     \
        dsi/libdsi.la           \
        nbp/libnbp.la           \
@@ -31,7 +35,7 @@ libatalk_la_DEPENDENCIES = \
        util/libutil.la         \
        tdb/libtdb.la       \
        unicode/libunicode.la \
-       vfs/libvfs.la @LIBATALK_ACLS@
+       vfs/libvfs.la
 
 libatalk_la_LDFLAGS = -static
 
index 9e9e63adf0202844858c05159253c4c282014bd7..e7f9753aca68a1400cd41dd409dc5744e8478463 100644 (file)
@@ -2,15 +2,12 @@
 
 noinst_HEADERS = aclldap.h cache.h
 
-if USE_NFSv4_ACLS
-
 noinst_LTLIBRARIES = libacl.la
-libacl_la_SOURCES = \
-       ldap.c          \
-       uuid.c          \
-       cache.c         \
-       ldap_config.c
-libacl_la_LDFLAGS = -lldap
+libacl_la_SOURCES = cache.c unix.c uuid.c
+libacl_la_LDFLAGS =
 
+if HAVE_LDAP
+libacl_la_SOURCES += ldap.c ldap_config.c
+libacl_la_LDFLAGS += -lldap
 endif
 
index 085ed0b4f28eda751a54f2969db1ec1a34272718..b4adea9f43a37874692a5b0c89468dbb26f82836 100644 (file)
@@ -1,5 +1,4 @@
 /*
-   $Id: aclldap.h,v 1.1 2009-02-02 11:55:01 franklahm Exp $
    Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
 
    This program is free software; you can redistribute it and/or modify
  * Interface
  ********************************************************/
 
-/* 
- *   name: give me his name
- *   type: and type of USER or GROUP
- *   uuid_string: returns pointer to allocated string
- * returns 0 on success !=0 on errror  
- */
 extern int ldap_getuuidfromname( const char *name, uuidtype_t type, char **uuid_string);
-
-/* 
- *   uuipd: give me his uuid
- *   name:  returns pointer to allocated string
- *   type:  returns type: USER or GROUP
- * returns 0 on success !=0 on errror
- */
-extern int ldap_getnamefromuuid( uuidp_t uuidp, char **name, uuidtype_t *type); 
+extern int ldap_getnamefromuuid( const char *uuidstr, char **name, uuidtype_t *type); 
 
 #endif /* ACLLDAP_H */
index 3b3a888f8682629393038d799b0782e9abe19cb4..585bb4801e8d4a727531f6ef3a4bff19f9d7c001 100644 (file)
@@ -1,5 +1,4 @@
 /*
-  $Id: cache.c,v 1.6 2010-04-23 11:37:05 franklahm Exp $
   Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
 
   This program is free software; you can redistribute it and/or modify
@@ -49,22 +48,19 @@ static int dumpcache() {
     int i;
     int ret = 0;
     cacheduser_t *entry;
-    char *uuidstring = NULL;
     char timestr[200];
     struct tm *tmp = NULL;
 
     for ( i=0 ; i<256; i++) {
         if ((entry = namecache[i]) != NULL) {
             do {
-                uuid_bin2string(entry->uuid, &uuidstring);
                 tmp = localtime(&entry->creationtime);
                 if (tmp == NULL)
                     continue;
                 if (strftime(timestr, 200, "%c", tmp) == 0)
                     continue;
                 LOG(log_debug9, logtype_default, "namecache{%d}: name:%s, uuid:%s, type: %s, cached: %s",
-                    i, entry->name, uuidstring, uuidtype[entry->type], timestr);
-                free(uuidstring);
+                    i, entry->name, uuid_bin2string(entry->uuid), uuidtype[entry->type], timestr);
             } while ((entry = entry->next) != NULL);
         }
     }
@@ -72,15 +68,14 @@ static int dumpcache() {
     for ( i=0; i<256; i++) {
         if ((entry = uuidcache[i]) != NULL) {
             do {
-                uuid_bin2string(entry->uuid, &uuidstring);
+
                 tmp = localtime(&entry->creationtime);
                 if (tmp == NULL)
                     continue;
                 if (strftime(timestr, 200, "%c", tmp) == 0)
                     continue;
                 LOG(log_debug9, logtype_default, "uuidcache{%d}: uuid:%s, name:%s, type: %s, cached: %s",
-                    i, uuidstring, entry->name, uuidtype[entry->type], timestr);
-                free(uuidstring);
+                    i, uuid_bin2string(entry->uuid), entry->name, uuidtype[entry->type], timestr);
             } while ((entry = entry->next) != NULL);
         }
     }
@@ -103,7 +98,7 @@ static unsigned char hashstring(unsigned char *str) {
     return index;
 }
 
-/* hash uuid_t into unsigned char */
+/* hash atalk_uuid_t into unsigned char */
 static unsigned char hashuuid(uuidp_t uuid) {
     unsigned char index = 83;
     int i;
@@ -122,9 +117,8 @@ static unsigned char hashuuid(uuidp_t uuid) {
 int add_cachebyname( const char *inname, const uuidp_t inuuid, const uuidtype_t type, const unsigned long uid _U_) {
     int ret = 0;
     char *name = NULL;
-    uuidp_t uuid;
+    uuidp_t uuid = NULL;
     cacheduser_t *cacheduser = NULL;
-    cacheduser_t *entry;
     unsigned char hash;
 
 #ifdef DEBUG
@@ -167,15 +161,14 @@ int add_cachebyname( const char *inname, const uuidp_t inuuid, const uuidtype_t
     /* get hash */
     hash = hashstring((unsigned char *)name);
 
-    /* insert cache entry into cache array */
-    if (namecache[hash] == NULL) { /* this queue is empty */
+    /* insert cache entry into cache array at head of queue */
+    if (namecache[hash] == NULL) {
+        /* this queue is empty */
+        namecache[hash] = cacheduser;
+    } else {
+        cacheduser->next = namecache[hash];
+        namecache[hash]->prev = cacheduser;
         namecache[hash] = cacheduser;
-    } else {            /* queue is not empty, search end of queue*/
-        entry = namecache[hash];
-        while( entry->next != NULL)
-            entry = entry->next;
-        cacheduser->prev = entry;
-        entry->next = cacheduser;
     }
 
 cleanup:
@@ -195,6 +188,9 @@ cleanup:
     return ret;
 }
 
+/* 
+ * Caller provides buffer uuid for result
+ */
 int search_cachebyname( const char *name, uuidtype_t type, uuidp_t uuid) {
     int ret;
     unsigned char hash;
@@ -207,7 +203,7 @@ int search_cachebyname( const char *name, uuidtype_t type, uuidp_t uuid) {
 
     hash = hashstring((unsigned char *)name);
 
-    if (! namecache[hash])
+    if (namecache[hash] == NULL)
         return -1;
 
     entry = namecache[hash];
@@ -219,9 +215,14 @@ int search_cachebyname( const char *name, uuidtype_t type, uuidp_t uuid) {
             if ((tim - entry->creationtime) > CACHESECONDS) {
                 LOG(log_debug, logtype_default, "search_cachebyname: expired: name:\'%s\' in queue {%d}", entry->name, hash);
                 /* remove item */
-                if (entry->prev) /* 2nd to last in queue */
+                if (entry->prev) {
+                    /* 2nd to last in queue */
                     entry->prev->next = entry->next;
-                else  { /* queue head */
+                    if (entry->next)
+                        /* not the last element */
+                        entry->next->prev = entry->prev;
+                } else  {
+                    /* queue head */
                     if ((namecache[hash] = entry->next) != NULL)
                         namecache[hash]->prev = NULL;
                 }
@@ -248,6 +249,9 @@ int search_cachebyname( const char *name, uuidtype_t type, uuidp_t uuid) {
     return -1;
 }
 
+/* 
+ * Caller must free allocated name
+ */
 int search_cachebyuuid( uuidp_t uuidp, char **name, uuidtype_t *type) {
     int ret;
     unsigned char hash;
@@ -270,9 +274,14 @@ int search_cachebyuuid( uuidp_t uuidp, char **name, uuidtype_t *type) {
             tim = time(NULL);
             if ((tim - entry->creationtime) > CACHESECONDS) {
                 LOG(log_debug, logtype_default, "search_cachebyuuid: expired: name:\'%s\' in queue {%d}", entry->name, hash);
-                if (entry->prev) /* 2nd to last in queue */
+                if (entry->prev) {
+                    /* 2nd to last in queue */
                     entry->prev->next = entry->next;
-                else { /* queue head  */
+                    if (entry->next)
+                        /* not the last element */
+                        entry->next->prev = entry->prev;
+                } else {
+                    /* queue head  */
                     if ((uuidcache[hash] = entry->next) != NULL)
                         uuidcache[hash]->prev = NULL;
                 }
@@ -306,7 +315,7 @@ int search_cachebyuuid( uuidp_t uuidp, char **name, uuidtype_t *type) {
 int add_cachebyuuid( uuidp_t inuuid, const char *inname, uuidtype_t type, const unsigned long uid _U_) {
     int ret = 0;
     char *name = NULL;
-    uuidp_t uuid;
+    uuidp_t uuid = NULL;
     cacheduser_t *cacheduser = NULL;
     cacheduser_t *entry;
     unsigned char hash;
@@ -351,15 +360,14 @@ int add_cachebyuuid( uuidp_t inuuid, const char *inname, uuidtype_t type, const
     /* get hash */
     hash = hashuuid(uuid);
 
-    /* insert cache entry into cache array */
-    if (uuidcache[hash] == NULL) { /* this queue is empty */
+    /* insert cache entry into cache array at head of queue */
+    if (uuidcache[hash] == NULL) {
+        /* this queue is empty */
+        uuidcache[hash] = cacheduser;
+    } else {
+        cacheduser->next = uuidcache[hash];
+        uuidcache[hash]->prev = cacheduser;
         uuidcache[hash] = cacheduser;
-    } else {            /* queue is not empty, search end of queue*/
-        entry = uuidcache[hash];
-        while( entry->next != NULL)
-            entry = entry->next;
-        cacheduser->prev = entry;
-        entry->next = cacheduser;
     }
 
 cleanup:
index 847f3e3299d04f607791642f8879c43d6af56790..724ac4d5ba8e59d59bf4aa2f73c2cc3450165765 100644 (file)
@@ -1,5 +1,4 @@
 /*
-  $Id: ldap.c,v 1.7 2010-04-23 11:37:06 franklahm Exp $
   Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
 
   This program is free software; you can redistribute it and/or modify
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
 
+#ifdef HAVE_LDAP
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/time.h>
 #include <string.h>
 #include <errno.h>
+#define LDAP_DEPRECATED 1
 #include <ldap.h>
 
 #include <atalk/logger.h>
@@ -90,6 +92,10 @@ struct pref_array prefs_array[] = {
  *   scope: LDAP_SCOPE_BASE, LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE
  *   result: return unique search result here, allocated here, caller must free
  *
+ * returns: -1 on error
+ *           0 nothing found
+ *           1 successfull search, result int 'result'
+ *
  * All connection managment to the LDAP server is done here. Just set KEEPALIVE if you know
  * you will be dispatching more than one search in a row, then don't set it with the last search.
  * You MUST dispatch the queries timely, otherwise the LDAP handle might timeout.
@@ -108,11 +114,10 @@ static int ldap_getattr_fromfilter_withbase_scope( const char *searchbase,
     static LDAP *ld     = NULL;
     LDAPMessage* msg    = NULL;
     LDAPMessage* entry  = NULL;
-
-    char **attribute_values;
+    char **attribute_values = NULL;
     struct timeval timeout;
 
-    LOG(log_maxdebug, logtype_afpd,"ldap_getattr_fromfilter_withbase_scope: BEGIN");
+    LOG(log_maxdebug, logtype_afpd,"ldap: BEGIN");
 
     timeout.tv_sec = 3;
     timeout.tv_usec = 0;
@@ -121,19 +126,21 @@ static int ldap_getattr_fromfilter_withbase_scope( const char *searchbase,
 retry:
     ret = 0;
 
-    if (!ldapconnected) {
-        LOG(log_maxdebug, logtype_default, "ldap_getattr_fromfilter_withbase_scope: LDAP server: \"%s\"",
+    if (ld == NULL) {
+        LOG(log_maxdebug, logtype_default, "ldap: server: \"%s\"",
             ldap_server);
         if ((ld = ldap_init(ldap_server, LDAP_PORT)) == NULL ) {
-            LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_init error");
+            LOG(log_error, logtype_default, "ldap: ldap_init error: %s",
+                strerror(errno));
             return -1;
         }
         if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &desired_version) != 0) {
             /* LDAP_OPT_SUCCESS is not in the proposed standard, so we check for 0
                http://tools.ietf.org/id/draft-ietf-ldapext-ldap-c-api-05.txt */
-            LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_set_option failed!");
-            ret = -1;
-            goto cleanup;
+            LOG(log_error, logtype_default, "ldap: ldap_set_option failed!");
+            free(ld);
+            ld = NULL;
+            return -1;
         }
     }
 
@@ -141,95 +148,98 @@ retry:
     if (!ldapconnected) {
         if (LDAP_AUTH_NONE == ldap_auth_method) {
             if (ldap_bind_s(ld, "", "", LDAP_AUTH_SIMPLE) != LDAP_SUCCESS ) {
-                LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_bind failed!");
-                LOG(log_error, logtype_default, "ldap_auth_method: \'%d\'", ldap_auth_method);
+                LOG(log_error, logtype_default, "ldap: ldap_bind failed, auth_method: \'%d\'",
+                    ldap_auth_method);
+                free(ld);
+                ld = NULL;
                 return -1;
             }
             ldapconnected = 1;
 
         } else if (LDAP_AUTH_SIMPLE == ldap_auth_method) {
             if (ldap_bind_s(ld, ldap_auth_dn, ldap_auth_pw, ldap_auth_method) != LDAP_SUCCESS ) {
-                LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_bind failed!");
-                LOG(log_error, logtype_default, "ldap_auth_dn: \'%s\', ldap_auth_pw: \'%s\', ldap_auth_method: \'%d\'",
+                LOG(log_error, logtype_default,
+                    "ldap: ldap_bind failed: ldap_auth_dn: \'%s\', ldap_auth_pw: \'%s\', ldap_auth_method: \'%d\'",
                     ldap_auth_dn, ldap_auth_pw, ldap_auth_method);
+                free(ld);
+                ld = NULL;
                 return -1;
             }
             ldapconnected = 1;
         }
     }
 
-    LOG(log_maxdebug, logtype_afpd, "LDAP start search: base: %s, filter: %s, attr: %s",
+    LOG(log_maxdebug, logtype_afpd, "ldap: start search: base: %s, filter: %s, attr: %s",
         searchbase, filter, attributes[0]);
 
     /* start LDAP search */
     ldaperr = ldap_search_st(ld, searchbase, scope, filter, attributes, 0, &timeout, &msg);
-    LOG(log_maxdebug, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_search_st returned: %s, %u",
-        ldap_err2string(ldaperr), ldaperr);
+    LOG(log_maxdebug, logtype_default, "ldap: ldap_search_st returned: %s",
+        ldap_err2string(ldaperr));
     if (ldaperr != LDAP_SUCCESS) {
-        if (retrycount ==1)
-            LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_search_st failed: %s", ldap_err2string(ldaperr));
+        LOG(log_error, logtype_default, "ldap: ldap_search_st failed: %s, retrycount: %i",
+            ldap_err2string(ldaperr), retrycount);
         ret = -1;
         goto cleanup;
     }
 
     /* parse search result */
-    LOG(log_maxdebug, logtype_default, "ldap_getuuidfromname: got %d entries from ldap search",
+    LOG(log_maxdebug, logtype_default, "ldap: got %d entries from ldap search",
         ldap_count_entries(ld, msg));
-    if (ldap_count_entries(ld, msg) != 1) {
-        ret = -1;
+    if ((ret = ldap_count_entries(ld, msg)) != 1) {
+        ret = 0;
         goto cleanup;
     }
 
     entry = ldap_first_entry(ld, msg);
     if (entry == NULL) {
-        LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: error in ldap_first_entry");
+        LOG(log_error, logtype_default, "ldap: ldap_first_entry error");
         ret = -1;
         goto cleanup;
     }
     attribute_values = ldap_get_values(ld, entry, attributes[0]);
     if (attribute_values == NULL) {
-        LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: error in ldap_get_values");
+        LOG(log_error, logtype_default, "ldap: ldap_get_values error");
         ret = -1;
         goto cleanup;
     }
 
-    LOG(log_maxdebug, logtype_afpd,"LDAP Search result: %s: %s",
+    LOG(log_maxdebug, logtype_afpd,"ldap: search result: %s: %s",
         attributes[0], attribute_values[0]);
 
-    /* allocate place for uuid as string */
-    *result = calloc( 1, strlen(attribute_values[0]) + 1);
+    /* allocate result */
+    *result = strdup(attribute_values[0]);
     if (*result == NULL) {
-        LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: %s: error calloc'ing",strerror(errno));
+        LOG(log_error, logtype_default, "ldap: strdup error: %s",strerror(errno));
         ret = -1;
         goto cleanup;
     }
-    /* get value */
-    strcpy( *result, attribute_values[0]);
-    ldap_value_free(attribute_values);
 
+cleanup:
+    if (attribute_values)
+        ldap_value_free(attribute_values);
     /* FIXME: is there another way to free entry ? */
     while (entry != NULL)
         entry = ldap_next_entry(ld, entry);
-
-cleanup:
     if (msg)
         ldap_msgfree(msg);
-    if (ld) {
-        if (ldapconnected
-            && ( !(conflags & KEEPALIVE)
-                 ||
-                 ((ret == -1) && (ldaperr != LDAP_SUCCESS))) /* ie ldapsearch got 0 results */
-            ) {
-
-            ldapconnected = 0;  /* regardless of unbind errors */
-            LOG(log_maxdebug, logtype_default,"LDAP unbind");
+
+    if (ldapconnected) {
+        if ((ret == -1) || !(conflags & KEEPALIVE)) {
+            LOG(log_maxdebug, logtype_default,"ldap: unbind");
             if (ldap_unbind_s(ld) != 0) {
-                LOG(log_error, logtype_default, "ldap_unbind_s: %s\n", ldap_err2string(ldaperr));
+                LOG(log_error, logtype_default, "ldap: unbind: %s\n", ldap_err2string(ldaperr));
                 return -1;
             }
-            retrycount++;
-            if (retrycount < 2)
-                goto retry;
+            ld = NULL;
+            ldapconnected = 0;
+
+            /* In case of error we try twice */
+            if (ret == -1) {
+                retrycount++;
+                if (retrycount < 2)
+                    goto retry;
+            }
         }
     }
     return ret;
@@ -239,6 +249,17 @@ cleanup:
  * Interface
  ********************************************************/
 
+/*! 
+ * Search UUID for name in LDAP
+ *
+ * Caller must free uuid_string when done with it
+ *
+ * @param name        (r) name to search
+ * @param type        (r) type of USER or GROUP
+ * @param uuid_string (w) result as pointer to allocated UUID-string
+ *
+ * @returns 0 on success, -1 on error or not found
+ */
 int ldap_getuuidfromname( const char *name, uuidtype_t type, char **uuid_string) {
     int ret;
     int len;
@@ -246,6 +267,9 @@ int ldap_getuuidfromname( const char *name, uuidtype_t type, char **uuid_string)
     char *attributes[]  = { ldap_uuid_attr, NULL};
     char *ldap_attr;
 
+    if (!ldap_config_valid)
+        return -1;
+
     /* make filter */
     if (type == UUID_GROUP)
         ldap_attr = ldap_group_attr;
@@ -262,15 +286,31 @@ int ldap_getuuidfromname( const char *name, uuidtype_t type, char **uuid_string)
     } else  { /* type hopefully == UUID_USER */
         ret = ldap_getattr_fromfilter_withbase_scope( ldap_userbase, filter, attributes, ldap_userscope, KEEPALIVE, uuid_string);
     }
-    return ret;
+    if (ret != 1)
+        return -1;
+    return 0;
 }
 
-int ldap_getnamefromuuid( char *uuidstr, char **name, uuidtype_t *type) {
+/*
+ * LDAP search wrapper
+ * returns allocated storage in name, caller must free it
+ * returns 0 on success, -1 on error or not found
+ * 
+ * @param uuidstr  (r) uuid to search as ascii string
+ * @param name     (w) return pointer to name as allocated string
+ * @param type     (w) return type: USER or GROUP
+ *
+ * returns 0 on success, -1 on errror
+ */
+int ldap_getnamefromuuid( const char *uuidstr, char **name, uuidtype_t *type) {
     int ret;
     int len;
     char filter[256];       /* this should really be enough. we dont want to malloc everything! */
     char *attributes[]  = { NULL, NULL};
 
+    if (!ldap_config_valid)
+        return -1;
+
     /* make filter */
     len = snprintf( filter, 256, "%s=%s", ldap_uuid_attr, uuidstr);
     if (len >= 256 || len == -1) {
@@ -280,16 +320,20 @@ int ldap_getnamefromuuid( char *uuidstr, char **name, uuidtype_t *type) {
     /* search groups first. group acls are probably used more often */
     attributes[0] = ldap_group_attr;
     ret = ldap_getattr_fromfilter_withbase_scope( ldap_groupbase, filter, attributes, ldap_groupscope, KEEPALIVE, name);
-    if (ret == 0) {
+    if (ret == -1)
+        return -1;
+    if (ret == 1) {
         *type = UUID_GROUP;
         return 0;
     }
+
     attributes[0] = ldap_name_attr;
     ret = ldap_getattr_fromfilter_withbase_scope( ldap_userbase, filter, attributes, ldap_userscope, KEEPALIVE, name);
-    if (ret == 0) {
+    if (ret == 1) {
         *type = UUID_USER;
         return 0;
     }
 
-    return ret;
+    return -1;
 }
+#endif  /* HAVE_LDAP */
index c302751b6eb850127b0689dd3bb9c4473eec8b0e..da37fb97027638370904806aba70aeef27003061 100644 (file)
@@ -1,5 +1,4 @@
 /*
-  $Id: ldap_config.c,v 1.4 2009-11-28 11:10:37 franklahm Exp $
   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
 
   This program is free software; you can redistribute it and/or modify
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
 
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_LDAP
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <errno.h>
 #include <string.h>
 #include <ctype.h>
@@ -124,7 +124,7 @@ int acl_ldap_readconfig(char *name)
 
     while(ldap_prefs[i].pref != NULL) {
         if ( ldap_prefs[i].valid != 0) {
-            LOG(log_error, logtype_afpd,"afp_ldap.conf: Missing option: \"%s\"", ldap_prefs[i].name);
+            LOG(log_debug, logtype_afpd,"afp_ldap.conf: Missing option: \"%s\"", ldap_prefs[i].name);
             ldap_config_valid = 0;
             break;
         }
@@ -133,16 +133,16 @@ int acl_ldap_readconfig(char *name)
 
     if (ldap_config_valid) {
         if (ldap_auth_method == LDAP_AUTH_NONE)
-            LOG(log_debug, logtype_afpd,"ldappref: Pref is ok. Using anonymous bind.");
+            LOG(log_debug, logtype_afpd,"afp_ldap.conf: Using anonymous bind.");
         else if (ldap_auth_method == LDAP_AUTH_SIMPLE)
-            LOG(log_debug, logtype_afpd,"ldappref: Pref is ok. Using simple bind.");
+            LOG(log_debug, logtype_afpd,"afp_ldap.conf: Using simple bind.");
         else {
             ldap_config_valid = 0;
-            LOG(log_error, logtype_afpd,"ldappref: Pref not ok. SASL not yet supported.");
+            LOG(log_error, logtype_afpd,"afp_ldap.conf: SASL not yet supported.");
         }
     } else
-        LOG(log_error, logtype_afpd,"ldappref: Pref is not ok.");
+        LOG(log_info, logtype_afpd,"afp_ldap.conf: not used");
     fclose(f);
     return 0;
 }
-#endif
+#endif /* HAVE_LDAP */
diff --git a/libatalk/acl/unix.c b/libatalk/acl/unix.c
new file mode 100644 (file)
index 0000000..f72d9aa
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+  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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#ifdef HAVE_SOLARIS_ACLS
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/acl.h>
+
+#include <atalk/logger.h>
+#include <atalk/afp.h>
+#include <atalk/util.h>
+#include <atalk/acl.h>
+
+/* Get ACL. Allocates storage as needed. Caller must free.
+ * Returns no of ACEs or -1 on error.  */
+int get_nfsv4_acl(const char *name, ace_t **retAces)
+{
+    int ace_count = -1;
+    ace_t *aces;
+    struct stat st;
+
+    *retAces = NULL;
+    /* Only call acl() for regular files and directories, otherwise just return 0 */
+    if (lstat(name, &st) != 0) {
+        LOG(log_warning, logtype_afpd, "get_nfsv4_acl(\"%s/%s\"): %s", getcwdpath(), name, strerror(errno));
+        return -1;
+    }
+
+    if (S_ISLNK(st.st_mode))
+        /* sorry, no ACLs for symlinks */
+        return 0;
+
+    if ( ! (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))) {
+        LOG(log_warning, logtype_afpd, "get_nfsv4_acl(\"%s/%s\"): special", getcwdpath(), name);
+        return 0;
+    }
+
+    if ((ace_count = acl(name, ACE_GETACLCNT, 0, NULL)) == 0) {
+        LOG(log_warning, logtype_afpd, "get_nfsv4_acl(\"%s/%s\"): 0 ACEs", getcwdpath(), name);
+        return 0;
+    }
+
+    if (ace_count == -1) {
+        LOG(log_error, logtype_afpd, "get_nfsv4_acl: acl('%s/%s', ACE_GETACLCNT): ace_count %i, error: %s",
+            getcwdpath(), name, ace_count, strerror(errno));
+        return -1;
+    }
+
+    aces = malloc(ace_count * sizeof(ace_t));
+    if (aces == NULL) {
+       LOG(log_error, logtype_afpd, "get_nfsv4_acl: malloc error");
+       return -1;
+    }
+
+    if ( (acl(name, ACE_GETACL, ace_count, aces)) == -1 ) {
+       LOG(log_error, logtype_afpd, "get_nfsv4_acl: acl(ACE_GETACL) error");
+       free(aces);
+       return -1;
+    }
+
+    LOG(log_debug9, logtype_afpd, "get_nfsv4_acl: file: %s -> No. of ACEs: %d", name, ace_count);
+    *retAces = aces;
+
+    return ace_count;
+}
+
+/*
+  Concatenate ACEs
+*/
+ace_t *concat_aces(ace_t *aces1, int ace1count, ace_t *aces2, int ace2count)
+{
+    ace_t *new_aces;
+    int i, j;
+
+    /* malloc buffer for new ACL */
+    if ((new_aces = malloc((ace1count + ace2count) * sizeof(ace_t))) == NULL) {
+        LOG(log_error, logtype_afpd, "combine_aces: malloc %s", strerror(errno));
+        return NULL;
+    }
+
+    /* Copy ACEs from buf1 */
+    for (i=0; i < ace1count; ) {
+        memcpy(&new_aces[i], &aces1[i], sizeof(ace_t));
+        i++;
+    }
+
+    j = i;
+
+    /* Copy ACEs from buf2 */
+    for (i=0; i < ace2count; ) {
+        memcpy(&new_aces[j], &aces2[i], sizeof(ace_t));
+        i++;
+        j++;
+    }
+    return new_aces;
+}
+
+/*
+  Remove any trivial ACE "in-place". Returns no of non-trivial ACEs
+*/
+int strip_trivial_aces(ace_t **saces, int sacecount)
+{
+    int i,j;
+    int nontrivaces = 0;
+    ace_t *aces = *saces;
+    ace_t *new_aces;
+
+    if (aces == NULL || sacecount <= 0)
+        return 0;
+
+    /* Count non-trivial ACEs */
+    for (i=0; i < sacecount; ) {
+        if ( ! (aces[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)))
+            nontrivaces++;
+        i++;
+    }
+    /* malloc buffer for new ACL */
+    if ((new_aces = malloc(nontrivaces * sizeof(ace_t))) == NULL) {
+        LOG(log_error, logtype_afpd, "strip_trivial_aces: malloc %s", strerror(errno));
+        return -1;
+    }
+
+    /* Copy non-trivial ACEs */
+    for (i=0, j=0; i < sacecount; ) {
+        if ( ! (aces[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE))) {
+            memcpy(&new_aces[j], &aces[i], sizeof(ace_t));
+            j++;
+        }
+        i++;
+    }
+
+    free(aces);
+    *saces = new_aces;
+
+    LOG(log_debug7, logtype_afpd, "strip_trivial_aces: non-trivial ACEs: %d", nontrivaces);
+
+    return nontrivaces;
+}
+
+/*
+  Remove non-trivial ACEs "in-place". Returns no of trivial ACEs.
+*/
+int strip_nontrivial_aces(ace_t **saces, int sacecount)
+{
+    int i,j;
+    int trivaces = 0;
+    ace_t *aces = *saces;
+    ace_t *new_aces;
+
+    /* Count trivial ACEs */
+    for (i=0; i < sacecount; ) {
+        if ((aces[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)))
+            trivaces++;
+        i++;
+    }
+    /* malloc buffer for new ACL */
+    if ((new_aces = malloc(trivaces * sizeof(ace_t))) == NULL) {
+        LOG(log_error, logtype_afpd, "strip_nontrivial_aces: malloc %s", strerror(errno));
+        return -1;
+    }
+
+    /* Copy trivial ACEs */
+    for (i=0, j=0; i < sacecount; ) {
+        if ((aces[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE))) {
+            memcpy(&new_aces[j], &aces[i], sizeof(ace_t));
+            j++;
+        }
+        i++;
+    }
+    /* Free old ACEs */
+    free(aces);
+    *saces = new_aces;
+
+    LOG(log_debug7, logtype_afpd, "strip_nontrivial_aces: trivial ACEs: %d", trivaces);
+
+    return trivaces;
+}
+
+/*!
+ * Change mode of file preserving existing explicit ACEs
+ *
+ * nfsv4_chmod
+ * (1) reads objects ACL (acl1)
+ * (2) removes all trivial ACEs from the ACL by calling strip_trivial_aces(), possibly
+ *     leaving 0 ACEs in the ACL if there were only trivial ACEs as mapped from the mode
+ * (3) calls chmod() with mode
+ * (4) reads the changed ACL (acl2) which
+ *     a) might still contain explicit ACEs (up to onnv132)
+ *     b) will have any explicit ACE removed (starting with onnv145/Openindiana)
+ * (5) strip any explicit ACE from acl2 using strip_nontrivial_aces()
+ * (6) merge acl2 and acl2
+ * (7) set the ACL merged ACL on the object
+ */
+int nfsv4_chmod(char *name, mode_t mode)
+{
+    int ret = -1;
+    int noaces, nnaces;
+    ace_t *oacl = NULL, *nacl = NULL, *cacl = NULL;
+
+    LOG(log_debug, logtype_afpd, "nfsv4_chmod(\"%s/%s\", %04o)",
+        getcwdpath(), name, mode);
+
+    if ((noaces = get_nfsv4_acl(name, &oacl)) == -1) /* (1) */
+        goto exit;
+    if ((noaces = strip_trivial_aces(&oacl, noaces)) == -1) /* (2) */
+        goto exit;
+
+#ifdef chmod
+#undef chmod
+#endif
+    if (chmod(name, mode) != 0) /* (3) */
+        goto exit;
+
+    if ((nnaces = get_nfsv4_acl(name, &nacl)) == -1) /* (4) */
+        goto exit;
+    if ((nnaces = strip_nontrivial_aces(&nacl, nnaces)) == -1) /* (5) */
+        goto exit;
+
+    if ((cacl = concat_aces(oacl, noaces, nacl, nnaces)) == NULL) /* (6) */
+        goto exit;
+
+    if ((ret = acl(name, ACE_SETACL, noaces + nnaces, cacl)) != 0) {
+        LOG(log_error, logtype_afpd, "nfsv4_chmod: error setting acl: %s", strerror(errno));
+        goto exit;
+    }
+
+exit:
+    if (oacl) free(oacl);
+    if (nacl) free(nacl);
+    if (cacl) free(cacl);
+
+    LOG(log_debug, logtype_afpd, "nfsv4_chmod(\"%s/%s\", %04o): result: %u",
+        getcwdpath(), name, mode, ret);
+
+    return ret;
+}
+
+#endif /* HAVE_SOLARIS_ACLS */
index 72c70c6f4124ef27d1df1b47db165fb850ef3b5a..074d9deb8a76c1da0343292b6250d940ee09d5c2 100644 (file)
@@ -1,17 +1,16 @@
 /*
-   $Id: uuid.c,v 1.3 2009-11-27 16:33:49 franklahm Exp $
-   Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
-
-   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.
- */
+  Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
+
+  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.
+*/
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
 
 #include <atalk/logger.h>
 #include <atalk/afp.h>
 #include <atalk/uuid.h>
+#include <atalk/util.h>
 
 #include "aclldap.h"
 #include "cache.h"
 
-char *uuidtype[] = {"NULL","USER", "GROUP"};
+char *uuidtype[] = {"NULL","USER", "GROUP", "LOCAL"};
 
 /********************************************************
  * Public helper function
  ********************************************************/
 
+static unsigned char local_group_uuid[] = {0xab, 0xcd, 0xef,
+                                           0xab, 0xcd, 0xef,
+                                           0xab, 0xcd, 0xef, 
+                                           0xab, 0xcd, 0xef};
+
+static unsigned char local_user_uuid[] = {0xff, 0xff, 0xee, 0xee, 0xdd, 0xdd,
+                                          0xcc, 0xcc, 0xbb, 0xbb, 0xaa, 0xaa};
+
+void localuuid_from_id(unsigned char *buf, uuidtype_t type, unsigned int id)
+{
+    uint32_t tmp;
+
+    switch (type) {
+    case UUID_GROUP:
+        memcpy(buf, local_group_uuid, 12);
+        break;
+    case UUID_USER:
+    default:
+        memcpy(buf, local_user_uuid, 12);
+        break;
+    }
+
+    tmp = htonl(id);
+    memcpy(buf + 12, &tmp, 4);
+
+    return;
+}
+
+/* 
+ * convert ascii string that can include dashes to binary uuid.
+ * caller must provide a buffer.
+ */
 void uuid_string2bin( const char *uuidstring, uuidp_t uuid) {
     int nibble = 1;
     int i = 0;
     unsigned char c, val = 0;
 
     while (*uuidstring) {
-       c = *uuidstring;
-       if (c == '-') {
-           uuidstring++;
-           continue;
-       }
-       else if (c <= '9')              /* 0-9 */
-           c -= '0';
-       else if (c <= 'F')      /* A-F */
-           c -= 'A' - 10;
-       else if (c <= 'f')      /* a-f */
-           c-= 'a' - 10;
-
-       if (nibble)
-           val = c * 16;
-       else
-           uuid[i++] = val + c;
-
-       nibble ^= 1;
-       uuidstring++;
+        c = *uuidstring;
+        if (c == '-') {
+            uuidstring++;
+            continue;
+        }
+        else if (c <= '9')      /* 0-9 */
+            c -= '0';
+        else if (c <= 'F')  /* A-F */
+            c -= 'A' - 10;
+        else if (c <= 'f')      /* a-f */
+            c-= 'a' - 10;
+
+        if (nibble)
+            val = c * 16;
+        else
+            uuid[i++] = val + c;
+
+        nibble ^= 1;
+        uuidstring++;
     }
 
 }
 
-int uuid_bin2string( uuidp_t uuid, char **uuidstring) {
-    char ascii[16] = { "0123456789ABCDEF" };
-    int nibble = 1;
+/*! 
+ * Convert 16 byte binary uuid to neat ascii represantation including dashes.
+ * 
+ * Returns pointer to static buffer.
+ */
+const char *uuid_bin2string(unsigned char *uuid) {
+    static char uuidstring[UUID_STRINGSIZE + 1];
+
     int i = 0;
     unsigned char c;
-    char *s;
-
-    *uuidstring = calloc(1, UUID_STRINGSIZE + 1);
-    if (*uuidstring == NULL) {
-        LOG(log_error, logtype_default, "uuid_bin2string: %s: error calloc'ing",strerror(errno));
-        return -1;
-    }
-    s = *uuidstring;
 
     while (i < UUID_STRINGSIZE) {
-       c = *uuid;
-       if (nibble)
-           c = c >> 4;
-       else {
-           c &= 0x0f;
-           uuid++;
-       }
-       s[i] = ascii[c];
-       nibble ^= 1;
-       i++;
-       if (i==8 || i==13 || i==18 || i==23)
-           s[i++] = '-';
+        c = *uuid;
+        uuid++;
+        sprintf(uuidstring + i, "%02X", c);
+        i += 2;
+        if (i==8 || i==13 || i==18 || i==23)
+            uuidstring[i++] = '-';
     }
-    return 0;
+    uuidstring[i] = 0;
+    return uuidstring;
 }
 
-/******************************************************** 
+/********************************************************
  * Interface
  ********************************************************/
 
+/*
+ *   name: give me his name
+ *   type: and type (UUID_USER or UUID_GROUP)
+ *   uuid: pointer to uuid_t storage that the caller must provide
+ * returns 0 on success !=0 on errror
+ */  
 int getuuidfromname( const char *name, uuidtype_t type, uuidp_t uuid) {
     int ret = 0;
+#ifdef HAVE_LDAP
     char *uuid_string = NULL;
-
+#endif
     ret = search_cachebyname( name, type, uuid);
-    if (ret == 0) {            /* found in cache */
-       uuid_bin2string( uuid, &uuid_string);
-       LOG(log_debug, logtype_afpd, "getuuidfromname{cache}: name: %s, type: %s -> UUID: %s",name, uuidtype[type], uuid_string);
-    } else  {                   /* if not found in cache */
-       ret = ldap_getuuidfromname( name, type, &uuid_string);
-       if (ret != 0) {
-           LOG(log_error, logtype_afpd, "getuuidfromname: no result from ldap_getuuidfromname");
-           goto cleanup;
-       }
-       uuid_string2bin( uuid_string, uuid);
-       add_cachebyname( name, uuid, type, 0);
-       LOG(log_debug, logtype_afpd, "getuuidfromname{LDAP}: name: %s, type: %s -> UUID: %s",name, uuidtype[type], uuid_string);
+    if (ret == 0) {
+        /* found in cache */
+        LOG(log_debug, logtype_afpd, "getuuidfromname{cache}: name: %s, type: %s -> UUID: %s",
+            name, uuidtype[type], uuid_bin2string(uuid));
+    } else  {
+        /* if not found in cache */
+#ifdef HAVE_LDAP
+        if ((ret = ldap_getuuidfromname( name, type, &uuid_string)) == 0) {
+            uuid_string2bin( uuid_string, uuid);
+            LOG(log_debug, logtype_afpd, "getuuidfromname{local}: name: %s, type: %s -> UUID: %s",
+                name, uuidtype[type], uuid_bin2string(uuid));
+        } else {
+            LOG(log_debug, logtype_afpd, "getuuidfromname(\"%s\",t:%u): no result from ldap search",
+                name, type);
+        }
+#endif
+        if (ret != 0) {
+            /* Build a local UUID */
+            if (type == UUID_USER) {
+                struct passwd *pwd;
+                if ((pwd = getpwnam(name)) == NULL) {
+                    LOG(log_error, logtype_afpd, "getuuidfromname(\"%s\",t:%u): unknown user",
+                        name, uuidtype[type]);
+                    goto cleanup;
+                }
+                localuuid_from_id(uuid, UUID_USER, pwd->pw_uid);
+            } else {
+                struct group *grp;
+                if ((grp = getgrnam(name)) == NULL) {
+                    LOG(log_error, logtype_afpd, "getuuidfromname(\"%s\",t:%u): unknown user",
+                        name, uuidtype[type]);
+                    goto cleanup;
+                }
+                localuuid_from_id(uuid, UUID_GROUP, grp->gr_gid);
+            }
+            LOG(log_debug, logtype_afpd, "getuuidfromname{local}: name: %s, type: %s -> UUID: %s",
+                name, uuidtype[type], uuid_bin2string(uuid));
+        }
+        ret = 0;
+        add_cachebyname( name, uuid, type, 0);
     }
 
 cleanup:
-    free(uuid_string);
+#ifdef HAVE_LDAP
+    if (uuid_string) free(uuid_string);
+#endif
     return ret;
 }
 
-int getnamefromuuid( uuidp_t uuidp, char **name, uuidtype_t *type) {
+
+/*
+ * uuidp: pointer to a uuid
+ * name: returns allocated buffer from ldap_getnamefromuuid
+ * type: returns USER, GROUP or LOCAL
+ * return 0 on success !=0 on errror
+ *
+ * Caller must free name appropiately.
+ */
+int getnamefromuuid(const uuidp_t uuidp, char **name, uuidtype_t *type) {
     int ret;
-    char *uuid_string = NULL;
-    
+
     ret = search_cachebyuuid( uuidp, name, type);
-    if (ret == 0) {            /* found in cache */
-#ifdef DEBUG
-       uuid_bin2string( uuidp, &uuid_string);
-       LOG(log_debug9, logtype_afpd, "getnamefromuuid{cache}: UUID: %s -> name: %s, type:%s", uuid_string, *name, uuidtype[*type]);
+    if (ret == 0) {
+        /* found in cache */
+        LOG(log_debug9, logtype_afpd, "getnamefromuuid{cache}: UUID: %s -> name: %s, type:%s",
+            uuid_bin2string(uuidp), *name, uuidtype[*type]);
+    } else {
+        /* not found in cache */
+
+        /* Check if UUID is a client local one */
+        if (memcmp(uuidp, local_user_uuid, 12) == 0
+            || memcmp(uuidp, local_group_uuid, 12) == 0) {
+            LOG(log_debug, logtype_afpd, "getnamefromuuid: local UUID: %" PRIu32 "",
+                ntohl(*(uint32_t *)(uuidp + 12)));
+            *type = UUID_LOCAL;
+            *name = strdup("UUID_LOCAL");
+            return 0;
+        }
+
+#ifdef HAVE_LDAP
+        ret = ldap_getnamefromuuid(uuid_bin2string(uuidp), name, type);
+        if (ret != 0) {
+            LOG(log_warning, logtype_afpd, "getnamefromuuid(%s): no result from ldap_getnamefromuuid",
+                uuid_bin2string(uuidp));
+            goto cleanup;
+        }
+        add_cachebyuuid( uuidp, *name, *type, 0);
+        LOG(log_debug, logtype_afpd, "getnamefromuuid{LDAP}: UUID: %s -> name: %s, type:%s",
+            uuid_bin2string(uuidp), *name, uuidtype[*type]);
 #endif
-    } else  {                   /* if not found in cache */
-       uuid_bin2string( uuidp, &uuid_string);
-       ret = ldap_getnamefromuuid( uuid_string, name, type);
-       if (ret != 0) {
-           LOG(log_error, logtype_afpd, "getnamefromuuid: no result from ldap_getuuidfromname");
-           goto cleanup;
-       }
-       add_cachebyuuid( uuidp, *name, *type, 0);
-       LOG(log_debug, logtype_afpd, "getnamefromuuid{LDAP}: UUID: %s -> name: %s, type:%s",uuid_string, *name, uuidtype[*type]);
     }
 
 cleanup:
-    free(uuid_string);
     return ret;
 }
index e90b6b8e0268c9ff41dc7c23e14ae5198fe49f87..123560b5683b3e4af2729ce02631f238c48ffe4a 100644 (file)
@@ -1,7 +1,3 @@
-/*
- * $Id: ad_attr.c,v 1.14 2010-01-06 14:05:15 franklahm Exp $
- */
-
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
@@ -64,7 +60,7 @@ int ad_getattr(const struct adouble *ad, u_int16_t *attr)
 /* ----------------- */
 int ad_setattr(const struct adouble *ad, const u_int16_t attribute)
 {
-    u_int16_t *fflags;
+    uint16_t fflags;
 
     /* we don't save open forks indicator */
     u_int16_t attr = attribute & ~htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN);
@@ -82,22 +78,24 @@ int ad_setattr(const struct adouble *ad, const u_int16_t attribute)
     }
 #if AD_VERSION == AD_VERSION2
     else if (ad->ad_version == AD_VERSION2) {
-        if (ad_getentryoff(ad, ADEID_AFPFILEI)) {
+        if (ad_getentryoff(ad, ADEID_AFPFILEI) && ad_getentryoff(ad, ADEID_FINDERI)) {
             memcpy(ad_entry(ad, ADEID_AFPFILEI) + AFPFILEIOFF_ATTR, &attr, sizeof(attr));
             
             /* Now set opaque flags in FinderInfo too */
-            fflags = (u_int16_t *)ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF;
+            memcpy(&fflags, ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, 2);
             if (attr & htons(ATTRBIT_INVISIBLE))
-                *fflags |= htons(FINDERINFO_INVISIBLE);
+                fflags |= htons(FINDERINFO_INVISIBLE);
             else
-                *fflags &= htons(~FINDERINFO_INVISIBLE);
+                fflags &= htons(~FINDERINFO_INVISIBLE);
 
             /* See above comment in ad_getattr() */
             if (attr & htons(ATTRBIT_MULTIUSER)) {
                 if ( ! (ad->ad_adflags & ADFLAGS_DIR) )
-                    *fflags |= htons(FINDERINFO_ISHARED);
+                    fflags |= htons(FINDERINFO_ISHARED);
             } else
-                    *fflags &= htons(~FINDERINFO_ISHARED);
+                    fflags &= htons(~FINDERINFO_ISHARED);
+
+            memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &fflags, 2);
         }
     }
 #endif
@@ -192,9 +190,12 @@ u_int32_t ad_forcegetid (struct adouble *adp)
  */
 int ad_setname(struct adouble *ad, const char *path)
 {
-    if (ad_getentryoff(ad, ADEID_NAME)) {
-        ad_setentrylen( ad, ADEID_NAME, strlen( path ));
-        memcpy(ad_entry( ad, ADEID_NAME ), path, ad_getentrylen( ad, ADEID_NAME ));
+    int len;
+    if ((len = strlen(path)) > ADEDLEN_NAME)
+        len = ADEDLEN_NAME;
+    if (path && ad_getentryoff(ad, ADEID_NAME)) {
+        ad_setentrylen( ad, ADEID_NAME, len);
+        memcpy(ad_entry( ad, ADEID_NAME ), path, len);
         return 1;
     }
     return 0;
index c3ab1b7738818e126baf0ae51980fa14a5bc5ff6..132c4f1ae7935b151b68ce34d269f3b91cd68616 100644 (file)
@@ -1,7 +1,3 @@
-/*
- * $Id: ad_date.c,v 1.5 2006-09-29 09:39:16 didg Exp $
- */
-
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
@@ -9,7 +5,7 @@
 #include <string.h>
 #include <atalk/adouble.h>
 
-int ad_setdate(const struct adouble *ad, 
+int ad_setdate(struct adouble *ad, 
               unsigned int dateoff, u_int32_t date)
 {
   int xlate = (dateoff & AD_DATE_UNIX);
index 5d99227a6fec5b6835319dc2bf81544c08756816..1f4d8b331b4bb92eafcb3a15c4d20dffe28aa6de 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: ad_flush.c,v 1.14 2010-03-30 12:55:26 franklahm Exp $
- *
  * Copyright (c) 1990,1991 Regents of The University of Michigan.
  * All Rights Reserved.
  *
@@ -199,9 +197,9 @@ int ad_close( struct adouble *ad, int adflags)
     int         err = 0;
 
     if ((adflags & ADFLAGS_DF)
-        && ad_data_fileno(ad) >= 0
+        && (ad_data_fileno(ad) >= 0 || ad_data_fileno(ad) == -2) /* -2 means symlink */
         && --ad->ad_data_fork.adf_refcount == 0) {
-        if (ad->ad_data_fork.adf_syml !=0) {
+        if (ad->ad_data_fork.adf_syml != NULL) {
             free(ad->ad_data_fork.adf_syml);
             ad->ad_data_fork.adf_syml = 0;
         } else {
index d5b58f8c954bd52ea1be7d5e90e4c84ccedab5b3..30a1303340c6fdef00a885c63d0ea00f5a7b8199 100644 (file)
@@ -1,6 +1,4 @@
 /* 
- * $Id: ad_lock.c,v 1.20 2010-03-30 12:55:26 franklahm Exp $
- *
  * Copyright (c) 1998,1999 Adrian Sun (asun@zoology.washington.edu)
  * All Rights Reserved. See COPYRIGHT for more information.
  *
@@ -338,12 +336,16 @@ int ad_fcntl_lock(struct adouble *ad, const u_int32_t eid, const int locktype,
      1) we're trying to re-lock a lock, but we didn't specify an update.
      2) we're trying to free only part of a lock. 
      3) we're trying to free a non-existent lock. */
-  if ((!adflock && (lock.l_type == F_UNLCK)) ||
-      (adflock && !(type & ADLOCK_UPGRADE) && 
-       ((lock.l_type != F_UNLCK) || (adflock->lock.l_start != lock.l_start) ||
-       (adflock->lock.l_len != lock.l_len)))) {
-    errno = EINVAL;
-    return -1;
+  if ( (!adflock && (lock.l_type == F_UNLCK))
+       ||
+       (adflock
+        && !(type & ADLOCK_UPGRADE)
+        && ((lock.l_type != F_UNLCK)
+            || (adflock->lock.l_start != lock.l_start)
+            || (adflock->lock.l_len != lock.l_len) ))
+      ) {
+      errno = EINVAL;
+      return -1;
   }
 
 
index 90d3e3627ec02459fca16673aa2aa02cbf2aeb9d..7adb4abda262c7261a8197463d36f9369a2a7426 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: ad_open.c,v 1.74 2010-04-13 08:05:06 franklahm Exp $
- *
  * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
  * Copyright (c) 1990,1991 Regents of The University of Michigan.
  * All Rights Reserved.
@@ -283,6 +281,7 @@ static int ad_update(struct adouble *ad, const char *path)
 
     /* last place for failure. */
     if (sys_ftruncate(fd, st.st_size + shiftdata) < 0) {
+        munmap(buf, st.st_size + shiftdata);
         goto bail_lock;
     }
 
@@ -1080,8 +1079,8 @@ ad_mkdir( const char *path, int mode)
     int st_invalid;
     struct stat stbuf;
 
-    LOG(log_debug, logtype_default, "ad_mkdir: creating ad-directory '%s/%s' with mode %04o",
-        getcwdpath(), path, mode);
+    LOG(log_debug, logtype_default, "ad_mkdir(\"%s\", %04o) {cwd: \"%s\"}",
+        path, mode, getcwdpath());
 
     st_invalid = ad_mode_st(path, &mode, &stbuf);
     ret = mkdir( path, mode );
@@ -1302,7 +1301,6 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble
                         return -1;
                     }
                     ad->ad_data_fork.adf_syml[lsz] = 0;
-                    ad->ad_data_fork.adf_syml = realloc(ad->ad_data_fork.adf_syml,lsz+1);
                     ad->ad_data_fork.adf_fd = -2; /* -2 means its a symlink */
                 }
             }
@@ -1383,7 +1381,8 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble
              * here.
              * if ((oflags & O_CREAT) ==> (oflags & O_RDWR)
              */
-            LOG(log_debug, logtype_default, "ad_open: creating new adouble file: %s/%s", getcwdpath(), ad_p);
+            LOG(log_debug, logtype_default, "ad_open(\"%s\"): {cwd: \"%s\"} creating adouble file",
+                ad_p, getcwdpath());
             admode = mode;
             errno = 0;
             st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
index 35ebc222675f33787ca59ced3e7214ab9624db6c..3031f6390ffa6dc485ba49dc10c55feebd849516 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: ad_write.c,v 1.11 2010-03-30 12:55:26 franklahm Exp $
- *
  * Copyright (c) 1990,1995 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
  */
@@ -82,8 +80,8 @@ ssize_t ad_write(struct adouble *ad, const u_int32_t eid, off_t off, const int e
        if (r_off < ad_getentryoff(ad, ADEID_RFORK)) {
            memcpy(ad->ad_data + r_off, buf, MIN(sizeof(ad->ad_data) -r_off, cc));
         }
-        if ( ad->ad_rlen  < r_off + cc ) {
-             ad->ad_rlen = r_off + cc;
+        if ( ad->ad_rlen off + cc ) {
+             ad->ad_rlen = off + cc;
         }
     }
     else {
index 51d87846820c0f34c92b36af9b5556faace25b98..5a1d585494af94c6811ff3efcf01714a3b9052d1 100644 (file)
@@ -266,6 +266,7 @@ ASP asp_getsession(ASP asp, server_child *server_children,
                            &(atp_sockaddr(asp->asp_atp)->sat_addr))) == NULL) 
          return NULL;
 
+    int dummy[2];
        switch ((pid = fork())) {
        case 0 : /* child */
          server_reset_signal();
@@ -296,29 +297,26 @@ ASP asp_getsession(ASP asp, server_child *server_children,
          break;
          
        default : /* parent process */
-         /* we need atomic setting or pb with tickle_handler 
-         */ 
-         switch (server_child_add(children, CHILD_ASPFORK, pid)) {
-         case 0: /* added child */
-           if ((asp_ac_tmp = (struct asp_child *) 
-                malloc(sizeof(struct asp_child)))) {
-             asp_ac_tmp->ac_pid = pid;
-             asp_ac_tmp->ac_state = ACSTATE_OK;
-             asp_ac_tmp->ac_sat = sat;
-             asp_ac_tmp->ac_sat.sat_port = asp->cmdbuf[1];
-             
-             asp->cmdbuf[0] = atp_sockaddr(atp)->sat_port;
-             asp->cmdbuf[1] = sid;
-             set_asp_ac(sid, asp_ac_tmp);
-             asperr = ASPERR_OK;
-             break;
-           } /* fall through if malloc fails */
-         case -1: /* bad error */
+         /* we need atomic setting or pb with tickle_handler */ 
+      if (server_child_add(children, CHILD_ASPFORK, pid, dummy)) {
+           if ((asp_ac_tmp = malloc(sizeof(struct asp_child))) == NULL) {
+            kill(pid, SIGQUIT); 
+            break;
+        }
+        asp_ac_tmp->ac_pid = pid;
+        asp_ac_tmp->ac_state = ACSTATE_OK;
+        asp_ac_tmp->ac_sat = sat;
+        asp_ac_tmp->ac_sat.sat_port = asp->cmdbuf[1];
+           
+        asp->cmdbuf[0] = atp_sockaddr(atp)->sat_port;
+        asp->cmdbuf[1] = sid;
+        set_asp_ac(sid, asp_ac_tmp);
+        asperr = ASPERR_OK;
+        break;
+      } else {
            kill(pid, SIGQUIT); 
            break;
-         default: /* non-fatal error */
-           break;
-         }
+      }
          atp_close(atp);
          break;
        }
diff --git a/libatalk/bstring/.gitignore b/libatalk/bstring/.gitignore
new file mode 100644 (file)
index 0000000..0d0371d
--- /dev/null
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+*.lo
+*.la
+.deps
+.libs
diff --git a/libatalk/bstring/Makefile.am b/libatalk/bstring/Makefile.am
new file mode 100644 (file)
index 0000000..39f78c3
--- /dev/null
@@ -0,0 +1,6 @@
+# Makefile.am for libatalk/adouble/
+
+noinst_LTLIBRARIES = libbstring.la
+
+libbstring_la_SOURCES = bstrlib.c bstradd.c
+
diff --git a/libatalk/bstring/bstradd.c b/libatalk/bstring/bstradd.c
new file mode 100644 (file)
index 0000000..7b59b2e
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+  $Id: bstradd.c,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
+  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+  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.
+*/
+
+/*!
+ * @file
+ * Additional functions for bstrlib
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <atalk/bstrlib.h>
+
+/* Optionally include a mechanism for debugging memory */
+
+#if defined(MEMORY_DEBUG) || defined(BSTRLIB_MEMORY_DEBUG)
+#include "memdbg.h"
+#endif
+
+#ifndef bstr__alloc
+#define bstr__alloc(x) malloc (x)
+#endif
+
+#ifndef bstr__free
+#define bstr__free(p) free (p)
+#endif
+
+#ifndef bstr__realloc
+#define bstr__realloc(p,x) realloc ((p), (x))
+#endif
+
+#ifndef bstr__memcpy
+#define bstr__memcpy(d,s,l) memcpy ((d), (s), (l))
+#endif
+
+#ifndef bstr__memmove
+#define bstr__memmove(d,s,l) memmove ((d), (s), (l))
+#endif
+
+#ifndef bstr__memset
+#define bstr__memset(d,c,l) memset ((d), (c), (l))
+#endif
+
+#ifndef bstr__memcmp
+#define bstr__memcmp(d,c,l) memcmp ((d), (c), (l))
+#endif
+
+#ifndef bstr__memchr
+#define bstr__memchr(s,c,l) memchr ((s), (c), (l))
+#endif
+
+/*************************************************************************
+ * Stuff for making bstrings referencing c-strings
+ ************************************************************************/
+
+/*!
+ * @brief Create a bstring referencing "str"
+ *
+ * This is usefull if a well know code path uses string, often doing strlen on string.
+ * By converting to bstring which carries the strlen, the repeated computation can be avoided.
+ */
+bstring brefcstr (char *str) {
+    bstring b;
+    int i;
+    size_t j;
+
+       if (str == NULL)
+        return NULL;
+       j = strlen(str);
+
+       b = (bstring)bstr__alloc(sizeof(struct tagbstring));
+       if (NULL == b)
+        return NULL;
+
+       b->slen = (int) j;
+    b->mlen = -1;
+    b->data = str;
+
+       return b;
+}
+
+/*!
+ * @brief Free up the bstring, WITHOUT freeing the pointed to c-string!
+ */
+int bunrefcstr (bstring b) {
+       if (b == NULL || b->slen < 0 || b->mlen > 0 || b->data == NULL)
+               return BSTR_ERR;
+
+       /* In case there is any stale usage, there is one more chance to 
+          notice this error. */
+
+       b->slen = -1;
+       b->mlen = -__LINE__;
+       b->data = NULL;
+
+       bstr__free (b);
+       return BSTR_OK;
+}
+
+/*************************************************************************
+ * stuff for bstrList
+ ************************************************************************/
+
+/*!
+ * @brief Create an empty list with preallocated storage for at least 'min' members
+ */
+struct bstrList *bstrListCreateMin(int min)
+{
+    struct bstrList *sl = NULL;
+
+    if ((sl = bstrListCreate()) == NULL)
+        return NULL;
+
+    if ((bstrListAlloc(sl, min)) != BSTR_OK) {
+        bstrListDestroy(sl);
+        return NULL;
+    }
+
+    return sl;
+}
+
+/*!
+ * @brief Push a bstring to the end of a list
+ */
+int bstrListPush(struct bstrList *sl, bstring bs)
+{
+    if (sl->qty == sl->mlen) {
+        if ((bstrListAlloc(sl, sl->qty + 1)) != BSTR_OK)
+            return BSTR_ERR;
+    }
+
+    sl->entry[sl->qty] = bs;
+    sl->qty++;
+    return BSTR_OK;
+}
+
+/*!
+ * @brief Pop a bstring from the end of a list
+ */
+bstring bstrListPop(struct bstrList *sl)
+{
+    return NULL;
+}
+
+/*!
+ * @brief Inverse bjoin
+ */
+bstring bjoinInv(const struct bstrList * bl, const_bstring sep) {
+    bstring b;
+    int i, j, c, v;
+
+    if (bl == NULL || bl->qty < 0)
+        return NULL;
+    if (sep != NULL && (sep->slen < 0 || sep->data == NULL))
+        return NULL;
+
+    for (i = 0, c = 1; i < bl->qty; i++) {
+        v = bl->entry[i]->slen;
+        if (v < 0)
+            return NULL;/* Invalid input */
+        c += v;
+        if (c < 0)
+            return NULL;/* Wrap around ?? */
+    }
+
+    if (sep != NULL)
+        c += (bl->qty - 1) * sep->slen;
+
+    b = (bstring) bstr__alloc (sizeof (struct tagbstring));
+    if (NULL == b)
+        return NULL; /* Out of memory */
+    b->data = (unsigned char *) bstr__alloc (c);
+    if (b->data == NULL) {
+        bstr__free (b);
+        return NULL;
+    }
+
+    b->mlen = c;
+    b->slen = c-1;
+
+    for (i = bl->qty - 1, c = 0, j = 0; i >= 0; i--, j++) {
+        if (j > 0 && sep != NULL) {
+            bstr__memcpy (b->data + c, sep->data, sep->slen);
+            c += sep->slen;
+        }
+        v = bl->entry[i]->slen;
+        bstr__memcpy (b->data + c, bl->entry[i]->data, v);
+        c += v;
+    }
+    b->data[c] = (unsigned char) '\0';
+    return b;
+}
diff --git a/libatalk/bstring/bstrlib.c b/libatalk/bstring/bstrlib.c
new file mode 100644 (file)
index 0000000..29e113e
--- /dev/null
@@ -0,0 +1,2956 @@
+/*
+ * This source file is part of the bstring string library.  This code was
+ * written by Paul Hsieh in 2002-2008, and is covered by the BSD open source 
+ * license and the GPL. Refer to the accompanying documentation for details 
+ * on usage and license.
+ */
+
+/*
+ * bstrlib.c
+ *
+ * This file is the core module for implementing the bstring functions.
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <atalk/bstrlib.h>
+
+/* Optionally include a mechanism for debugging memory */
+
+#if defined(MEMORY_DEBUG) || defined(BSTRLIB_MEMORY_DEBUG)
+#include "memdbg.h"
+#endif
+
+#ifndef bstr__alloc
+#define bstr__alloc(x) malloc (x)
+#endif
+
+#ifndef bstr__free
+#define bstr__free(p) free (p)
+#endif
+
+#ifndef bstr__realloc
+#define bstr__realloc(p,x) realloc ((p), (x))
+#endif
+
+#ifndef bstr__memcpy
+#define bstr__memcpy(d,s,l) memcpy ((d), (s), (l))
+#endif
+
+#ifndef bstr__memmove
+#define bstr__memmove(d,s,l) memmove ((d), (s), (l))
+#endif
+
+#ifndef bstr__memset
+#define bstr__memset(d,c,l) memset ((d), (c), (l))
+#endif
+
+#ifndef bstr__memcmp
+#define bstr__memcmp(d,c,l) memcmp ((d), (c), (l))
+#endif
+
+#ifndef bstr__memchr
+#define bstr__memchr(s,c,l) memchr ((s), (c), (l))
+#endif
+
+/* Just a length safe wrapper for memmove. */
+
+#define bBlockCopy(D,S,L) { if ((L) > 0) bstr__memmove ((D),(S),(L)); }
+
+/* Compute the snapped size for a given requested size.  By snapping to powers
+   of 2 like this, repeated reallocations are avoided. */
+static int snapUpSize (int i) {
+       if (i < 8) {
+               i = 8;
+       } else {
+               unsigned int j;
+               j = (unsigned int) i;
+
+               j |= (j >>  1);
+               j |= (j >>  2);
+               j |= (j >>  4);
+               j |= (j >>  8);         /* Ok, since int >= 16 bits */
+#if (UINT_MAX != 0xffff)
+               j |= (j >> 16);         /* For 32 bit int systems */
+#if (UINT_MAX > 0xffffffffUL)
+               j |= (j >> 32);         /* For 64 bit int systems */
+#endif
+#endif
+               /* Least power of two greater than i */
+               j++;
+               if ((int) j >= i) i = (int) j;
+       }
+       return i;
+}
+
+/*  int balloc (bstring b, int len)
+ *
+ *  Increase the size of the memory backing the bstring b to at least len.
+ */
+int balloc (bstring b, int olen) {
+       int len;
+       if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen <= 0 || 
+           b->mlen < b->slen || olen <= 0) {
+               return BSTR_ERR;
+       }
+
+       if (olen >= b->mlen) {
+               unsigned char * x;
+
+               if ((len = snapUpSize (olen)) <= b->mlen) return BSTR_OK;
+
+               /* Assume probability of a non-moving realloc is 0.125 */
+               if (7 * b->mlen < 8 * b->slen) {
+
+                       /* If slen is close to mlen in size then use realloc to reduce
+                          the memory defragmentation */
+
+                       reallocStrategy:;
+
+                       x = (unsigned char *) bstr__realloc (b->data, (size_t) len);
+                       if (x == NULL) {
+
+                               /* Since we failed, try allocating the tighest possible 
+                                  allocation */
+
+                               if (NULL == (x = (unsigned char *) bstr__realloc (b->data, (size_t) (len = olen)))) {
+                                       return BSTR_ERR;
+                               }
+                       }
+               } else {
+
+                       /* If slen is not close to mlen then avoid the penalty of copying
+                          the extra bytes that are allocated, but not considered part of
+                          the string */
+
+                       if (NULL == (x = (unsigned char *) bstr__alloc ((size_t) len))) {
+
+                               /* Perhaps there is no available memory for the two 
+                                  allocations to be in memory at once */
+
+                               goto reallocStrategy;
+
+                       } else {
+                               if (b->slen) bstr__memcpy ((char *) x, (char *) b->data, (size_t) b->slen);
+                               bstr__free (b->data);
+                       }
+               }
+               b->data = x;
+               b->mlen = len;
+               b->data[b->slen] = (unsigned char) '\0';
+       }
+
+       return BSTR_OK;
+}
+
+/*  int ballocmin (bstring b, int len)
+ *
+ *  Set the size of the memory backing the bstring b to len or b->slen+1,
+ *  whichever is larger.  Note that repeated use of this function can degrade
+ *  performance.
+ */
+int ballocmin (bstring b, int len) {
+       unsigned char * s;
+
+       if (b == NULL || b->data == NULL || (b->slen+1) < 0 || b->mlen <= 0 || 
+           b->mlen < b->slen || len <= 0) {
+               return BSTR_ERR;
+       }
+
+       if (len < b->slen + 1) len = b->slen + 1;
+
+       if (len != b->mlen) {
+               s = (unsigned char *) bstr__realloc (b->data, (size_t) len);
+               if (NULL == s) return BSTR_ERR;
+               s[b->slen] = (unsigned char) '\0';
+               b->data = s;
+               b->mlen = len;
+       }
+
+       return BSTR_OK;
+}
+
+/*  bstring bfromcstr (const char * str)
+ *
+ *  Create a bstring which contains the contents of the '\0' terminated char *
+ *  buffer str.
+ */
+bstring bfromcstr (const char * str) {
+bstring b;
+int i;
+size_t j;
+
+       if (str == NULL) return NULL;
+       j = (strlen) (str);
+       i = snapUpSize ((int) (j + (2 - (j != 0))));
+       if (i <= (int) j) return NULL;
+
+       b = (bstring) bstr__alloc (sizeof (struct tagbstring));
+       if (NULL == b) return NULL;
+       b->slen = (int) j;
+       if (NULL == (b->data = (unsigned char *) bstr__alloc (b->mlen = i))) {
+               bstr__free (b);
+               return NULL;
+       }
+
+       bstr__memcpy (b->data, str, j+1);
+       return b;
+}
+
+/*  bstring bfromcstralloc (int mlen, const char * str)
+ *
+ *  Create a bstring which contains the contents of the '\0' terminated char *
+ *  buffer str.  The memory buffer backing the string is at least len 
+ *  characters in length.
+ */
+bstring bfromcstralloc (int mlen, const char * str) {
+bstring b;
+int i;
+size_t j;
+
+       if (str == NULL) return NULL;
+       j = (strlen) (str);
+       i = snapUpSize ((int) (j + (2 - (j != 0))));
+       if (i <= (int) j) return NULL;
+
+       b = (bstring) bstr__alloc (sizeof (struct tagbstring));
+       if (b == NULL) return NULL;
+       b->slen = (int) j;
+       if (i < mlen) i = mlen;
+
+       if (NULL == (b->data = (unsigned char *) bstr__alloc (b->mlen = i))) {
+               bstr__free (b);
+               return NULL;
+       }
+
+       bstr__memcpy (b->data, str, j+1);
+       return b;
+}
+
+/*  bstring blk2bstr (const void * blk, int len)
+ *
+ *  Create a bstring which contains the content of the block blk of length 
+ *  len.
+ */
+bstring blk2bstr (const void * blk, int len) {
+bstring b;
+int i;
+
+       if (blk == NULL || len < 0) return NULL;
+       b = (bstring) bstr__alloc (sizeof (struct tagbstring));
+       if (b == NULL) return NULL;
+       b->slen = len;
+
+       i = len + (2 - (len != 0));
+       i = snapUpSize (i);
+
+       b->mlen = i;
+
+       b->data = (unsigned char *) bstr__alloc ((size_t) b->mlen);
+       if (b->data == NULL) {
+               bstr__free (b);
+               return NULL;
+       }
+
+       if (len > 0) bstr__memcpy (b->data, blk, (size_t) len);
+       b->data[len] = (unsigned char) '\0';
+
+       return b;
+}
+
+/*  char * bstr2cstr (const_bstring s, char z)
+ *
+ *  Create a '\0' terminated char * buffer which is equal to the contents of 
+ *  the bstring s, except that any contained '\0' characters are converted 
+ *  to the character in z. This returned value should be freed with a 
+ *  bcstrfree () call, by the calling application.
+ */
+char * bstr2cstr (const_bstring b, char z) {
+int i, l;
+char * r;
+
+       if (b == NULL || b->slen < 0 || b->data == NULL) return NULL;
+       l = b->slen;
+       r = (char *) bstr__alloc ((size_t) (l + 1));
+       if (r == NULL) return r;
+
+       for (i=0; i < l; i ++) {
+               r[i] = (char) ((b->data[i] == '\0') ? z : (char) (b->data[i]));
+       }
+
+       r[l] = (unsigned char) '\0';
+
+       return r;
+}
+
+/*  int bcstrfree (char * s)
+ *
+ *  Frees a C-string generated by bstr2cstr ().  This is normally unnecessary
+ *  since it just wraps a call to bstr__free (), however, if bstr__alloc () 
+ *  and bstr__free () have been redefined as a macros within the bstrlib 
+ *  module (via defining them in memdbg.h after defining 
+ *  BSTRLIB_MEMORY_DEBUG) with some difference in behaviour from the std 
+ *  library functions, then this allows a correct way of freeing the memory 
+ *  that allows higher level code to be independent from these macro 
+ *  redefinitions.
+ */
+int bcstrfree (char * s) {
+       if (s) {
+               bstr__free (s);
+               return BSTR_OK;
+       }
+       return BSTR_ERR;
+}
+
+/*  int bconcat (bstring b0, const_bstring b1)
+ *
+ *  Concatenate the bstring b1 to the bstring b0.
+ */
+int bconcat (bstring b0, const_bstring b1) {
+int len, d;
+bstring aux = (bstring) b1;
+
+       if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL) return BSTR_ERR;
+
+       d = b0->slen;
+       len = b1->slen;
+       if ((d | (b0->mlen - d) | len | (d + len)) < 0) return BSTR_ERR;
+
+       if (b0->mlen <= d + len + 1) {
+               ptrdiff_t pd = b1->data - b0->data;
+               if (0 <= pd && pd < b0->mlen) {
+                       if (NULL == (aux = bstrcpy (b1))) return BSTR_ERR;
+               }
+               if (balloc (b0, d + len + 1) != BSTR_OK) {
+                       if (aux != b1) bdestroy (aux);
+                       return BSTR_ERR;
+               }
+       }
+
+       bBlockCopy (&b0->data[d], &aux->data[0], (size_t) len);
+       b0->data[d + len] = (unsigned char) '\0';
+       b0->slen = d + len;
+       if (aux != b1) bdestroy (aux);
+       return BSTR_OK;
+}
+
+/*  int bconchar (bstring b, char c)
+/ *
+ *  Concatenate the single character c to the bstring b.
+ */
+int bconchar (bstring b, char c) {
+int d;
+
+       if (b == NULL) return BSTR_ERR;
+       d = b->slen;
+       if ((d | (b->mlen - d)) < 0 || balloc (b, d + 2) != BSTR_OK) return BSTR_ERR;
+       b->data[d] = (unsigned char) c;
+       b->data[d + 1] = (unsigned char) '\0';
+       b->slen++;
+       return BSTR_OK;
+}
+
+/*  int bcatcstr (bstring b, const char * s)
+ *
+ *  Concatenate a char * string to a bstring.
+ */
+int bcatcstr (bstring b, const char * s) {
+char * d;
+int i, l;
+
+       if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen < b->slen
+        || b->mlen <= 0 || s == NULL) return BSTR_ERR;
+
+       /* Optimistically concatenate directly */
+       l = b->mlen - b->slen;
+       d = (char *) &b->data[b->slen];
+       for (i=0; i < l; i++) {
+               if ((*d++ = *s++) == '\0') {
+                       b->slen += i;
+                       return BSTR_OK;
+               }
+       }
+       b->slen += i;
+
+       /* Need to explicitely resize and concatenate tail */
+       return bcatblk (b, (const void *) s, (int) strlen (s));
+}
+
+/*  int bcatblk (bstring b, const void * s, int len)
+ *
+ *  Concatenate a fixed length buffer to a bstring.
+ */
+int bcatblk (bstring b, const void * s, int len) {
+int nl;
+
+       if (b == NULL || b->data == NULL || b->slen < 0 || b->mlen < b->slen
+        || b->mlen <= 0 || s == NULL || len < 0) return BSTR_ERR;
+
+       if (0 > (nl = b->slen + len)) return BSTR_ERR; /* Overflow? */
+       if (b->mlen <= nl && 0 > balloc (b, nl + 1)) return BSTR_ERR;
+
+       bBlockCopy (&b->data[b->slen], s, (size_t) len);
+       b->slen = nl;
+       b->data[nl] = (unsigned char) '\0';
+       return BSTR_OK;
+}
+
+/*  bstring bstrcpy (const_bstring b)
+ *
+ *  Create a copy of the bstring b.
+ */
+bstring bstrcpy (const_bstring b) {
+bstring b0;
+int i,j;
+
+       /* Attempted to copy an invalid string? */
+       if (b == NULL || b->slen < 0 || b->data == NULL) return NULL;
+
+       b0 = (bstring) bstr__alloc (sizeof (struct tagbstring));
+       if (b0 == NULL) {
+               /* Unable to allocate memory for string header */
+               return NULL;
+       }
+
+       i = b->slen;
+       j = snapUpSize (i + 1);
+
+       b0->data = (unsigned char *) bstr__alloc (j);
+       if (b0->data == NULL) {
+               j = i + 1;
+               b0->data = (unsigned char *) bstr__alloc (j);
+               if (b0->data == NULL) {
+                       /* Unable to allocate memory for string data */
+                       bstr__free (b0);
+                       return NULL;
+               }
+       }
+
+       b0->mlen = j;
+       b0->slen = i;
+
+       if (i) bstr__memcpy ((char *) b0->data, (char *) b->data, i);
+       b0->data[b0->slen] = (unsigned char) '\0';
+
+       return b0;
+}
+
+/*  int bassign (bstring a, const_bstring b)
+ *
+ *  Overwrite the string a with the contents of string b.
+ */
+int bassign (bstring a, const_bstring b) {
+       if (b == NULL || b->data == NULL || b->slen < 0)
+               return BSTR_ERR;
+       if (b->slen != 0) {
+               if (balloc (a, b->slen) != BSTR_OK) return BSTR_ERR;
+               bstr__memmove (a->data, b->data, b->slen);
+       } else {
+               if (a == NULL || a->data == NULL || a->mlen < a->slen || 
+                   a->slen < 0 || a->mlen == 0) 
+                       return BSTR_ERR;
+       }
+       a->data[b->slen] = (unsigned char) '\0';
+       a->slen = b->slen;
+       return BSTR_OK;
+}
+
+/*  int bassignmidstr (bstring a, const_bstring b, int left, int len)
+ *
+ *  Overwrite the string a with the middle of contents of string b 
+ *  starting from position left and running for a length len.  left and 
+ *  len are clamped to the ends of b as with the function bmidstr.
+ */
+int bassignmidstr (bstring a, const_bstring b, int left, int len) {
+       if (b == NULL || b->data == NULL || b->slen < 0)
+               return BSTR_ERR;
+
+       if (left < 0) {
+               len += left;
+               left = 0;
+       }
+
+       if (len > b->slen - left) len = b->slen - left;
+
+       if (a == NULL || a->data == NULL || a->mlen < a->slen ||
+           a->slen < 0 || a->mlen == 0)
+               return BSTR_ERR;
+
+       if (len > 0) {
+               if (balloc (a, len) != BSTR_OK) return BSTR_ERR;
+               bstr__memmove (a->data, b->data + left, len);
+               a->slen = len;
+       } else {
+               a->slen = 0;
+       }
+       a->data[a->slen] = (unsigned char) '\0';
+       return BSTR_OK;
+}
+
+/*  int bassigncstr (bstring a, const char * str)
+ *
+ *  Overwrite the string a with the contents of char * string str.  Note that 
+ *  the bstring a must be a well defined and writable bstring.  If an error 
+ *  occurs BSTR_ERR is returned however a may be partially overwritten.
+ */
+int bassigncstr (bstring a, const char * str) {
+int i;
+size_t len;
+       if (a == NULL || a->data == NULL || a->mlen < a->slen ||
+           a->slen < 0 || a->mlen == 0 || NULL == str) 
+               return BSTR_ERR;
+
+       for (i=0; i < a->mlen; i++) {
+               if ('\0' == (a->data[i] = str[i])) {
+                       a->slen = i;
+                       return BSTR_OK;
+               }
+       }
+
+       a->slen = i;
+       len = strlen (str + i);
+       if (len > INT_MAX || i + len + 1 > INT_MAX ||
+           0 > balloc (a, (int) (i + len + 1))) return BSTR_ERR;
+       bBlockCopy (a->data + i, str + i, (size_t) len + 1);
+       a->slen += (int) len;
+       return BSTR_OK;
+}
+
+/*  int bassignblk (bstring a, const void * s, int len)
+ *
+ *  Overwrite the string a with the contents of the block (s, len).  Note that 
+ *  the bstring a must be a well defined and writable bstring.  If an error 
+ *  occurs BSTR_ERR is returned and a is not overwritten.
+ */
+int bassignblk (bstring a, const void * s, int len) {
+       if (a == NULL || a->data == NULL || a->mlen < a->slen ||
+           a->slen < 0 || a->mlen == 0 || NULL == s || len + 1 < 1) 
+               return BSTR_ERR;
+       if (len + 1 > a->mlen && 0 > balloc (a, len + 1)) return BSTR_ERR;
+       bBlockCopy (a->data, s, (size_t) len);
+       a->data[len] = (unsigned char) '\0';
+       a->slen = len;
+       return BSTR_OK;
+}
+
+/*  int btrunc (bstring b, int n)
+ *
+ *  Truncate the bstring to at most n characters.
+ */
+int btrunc (bstring b, int n) {
+       if (n < 0 || b == NULL || b->data == NULL || b->mlen < b->slen ||
+           b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+       if (b->slen > n) {
+               b->slen = n;
+               b->data[n] = (unsigned char) '\0';
+       }
+       return BSTR_OK;
+}
+
+#define   upcase(c) (toupper ((unsigned char) c))
+#define downcase(c) (tolower ((unsigned char) c))
+#define   wspace(c) (isspace ((unsigned char) c))
+
+/*  int btoupper (bstring b)
+ *
+ *  Convert contents of bstring to upper case.
+ */
+int btoupper (bstring b) {
+int i, len;
+       if (b == NULL || b->data == NULL || b->mlen < b->slen ||
+           b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+       for (i=0, len = b->slen; i < len; i++) {
+               b->data[i] = (unsigned char) upcase (b->data[i]);
+       }
+       return BSTR_OK;
+}
+
+/*  int btolower (bstring b)
+ *
+ *  Convert contents of bstring to lower case.
+ */
+int btolower (bstring b) {
+int i, len;
+       if (b == NULL || b->data == NULL || b->mlen < b->slen ||
+           b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+       for (i=0, len = b->slen; i < len; i++) {
+               b->data[i] = (unsigned char) downcase (b->data[i]);
+       }
+       return BSTR_OK;
+}
+
+/*  int bstricmp (const_bstring b0, const_bstring b1)
+ *
+ *  Compare two strings without differentiating between case.  The return 
+ *  value is the difference of the values of the characters where the two 
+ *  strings first differ after lower case transformation, otherwise 0 is 
+ *  returned indicating that the strings are equal.  If the lengths are 
+ *  different, then a difference from 0 is given, but if the first extra 
+ *  character is '\0', then it is taken to be the value UCHAR_MAX+1.
+ */
+int bstricmp (const_bstring b0, const_bstring b1) {
+int i, v, n;
+
+       if (bdata (b0) == NULL || b0->slen < 0 || 
+           bdata (b1) == NULL || b1->slen < 0) return SHRT_MIN;
+       if ((n = b0->slen) > b1->slen) n = b1->slen;
+       else if (b0->slen == b1->slen && b0->data == b1->data) return BSTR_OK;
+
+       for (i = 0; i < n; i ++) {
+               v  = (char) downcase (b0->data[i])
+                  - (char) downcase (b1->data[i]);
+               if (0 != v) return v;
+       }
+
+       if (b0->slen > n) {
+               v = (char) downcase (b0->data[n]);
+               if (v) return v;
+               return UCHAR_MAX + 1;
+       }
+       if (b1->slen > n) {
+               v = - (char) downcase (b1->data[n]);
+               if (v) return v;
+               return - (int) (UCHAR_MAX + 1);
+       }
+       return BSTR_OK;
+}
+
+/*  int bstrnicmp (const_bstring b0, const_bstring b1, int n)
+ *
+ *  Compare two strings without differentiating between case for at most n
+ *  characters.  If the position where the two strings first differ is
+ *  before the nth position, the return value is the difference of the values
+ *  of the characters, otherwise 0 is returned.  If the lengths are different
+ *  and less than n characters, then a difference from 0 is given, but if the 
+ *  first extra character is '\0', then it is taken to be the value 
+ *  UCHAR_MAX+1.
+ */
+int bstrnicmp (const_bstring b0, const_bstring b1, int n) {
+int i, v, m;
+
+       if (bdata (b0) == NULL || b0->slen < 0 || 
+           bdata (b1) == NULL || b1->slen < 0 || n < 0) return SHRT_MIN;
+       m = n;
+       if (m > b0->slen) m = b0->slen;
+       if (m > b1->slen) m = b1->slen;
+
+       if (b0->data != b1->data) {
+               for (i = 0; i < m; i ++) {
+                       v  = (char) downcase (b0->data[i]);
+                       v -= (char) downcase (b1->data[i]);
+                       if (v != 0) return b0->data[i] - b1->data[i];
+               }
+       }
+
+       if (n == m || b0->slen == b1->slen) return BSTR_OK;
+
+       if (b0->slen > m) {
+               v = (char) downcase (b0->data[m]);
+               if (v) return v;
+               return UCHAR_MAX + 1;
+       }
+
+       v = - (char) downcase (b1->data[m]);
+       if (v) return v;
+       return - (int) (UCHAR_MAX + 1);
+}
+
+/*  int biseqcaseless (const_bstring b0, const_bstring b1)
+ *
+ *  Compare two strings for equality without differentiating between case.  
+ *  If the strings differ other than in case, 0 is returned, if the strings 
+ *  are the same, 1 is returned, if there is an error, -1 is returned.  If 
+ *  the length of the strings are different, this function is O(1).  '\0' 
+ *  termination characters are not treated in any special way.
+ */
+int biseqcaseless (const_bstring b0, const_bstring b1) {
+int i, n;
+
+       if (bdata (b0) == NULL || b0->slen < 0 || 
+           bdata (b1) == NULL || b1->slen < 0) return BSTR_ERR;
+       if (b0->slen != b1->slen) return BSTR_OK;
+       if (b0->data == b1->data || b0->slen == 0) return 1;
+       for (i=0, n=b0->slen; i < n; i++) {
+               if (b0->data[i] != b1->data[i]) {
+                       unsigned char c = (unsigned char) downcase (b0->data[i]);
+                       if (c != (unsigned char) downcase (b1->data[i])) return 0;
+               }
+       }
+       return 1;
+}
+
+/*  int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len)
+ *
+ *  Compare beginning of string b0 with a block of memory of length len 
+ *  without differentiating between case for equality.  If the beginning of b0
+ *  differs from the memory block other than in case (or if b0 is too short), 
+ *  0 is returned, if the strings are the same, 1 is returned, if there is an 
+ *  error, -1 is returned.  '\0' characters are not treated in any special 
+ *  way.
+ */
+int bisstemeqcaselessblk (const_bstring b0, const void * blk, int len) {
+int i;
+
+       if (bdata (b0) == NULL || b0->slen < 0 || NULL == blk || len < 0)
+               return BSTR_ERR;
+       if (b0->slen < len) return BSTR_OK;
+       if (b0->data == (const unsigned char *) blk || len == 0) return 1;
+
+       for (i = 0; i < len; i ++) {
+               if (b0->data[i] != ((const unsigned char *) blk)[i]) {
+                       if (downcase (b0->data[i]) != 
+                           downcase (((const unsigned char *) blk)[i])) return 0;
+               }
+       }
+       return 1;
+}
+
+/*
+ * int bltrimws (bstring b)
+ *
+ * Delete whitespace contiguous from the left end of the string.
+ */
+int bltrimws (bstring b) {
+int i, len;
+
+       if (b == NULL || b->data == NULL || b->mlen < b->slen ||
+           b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+
+       for (len = b->slen, i = 0; i < len; i++) {
+               if (!wspace (b->data[i])) {
+                       return bdelete (b, 0, i);
+               }
+       }
+
+       b->data[0] = (unsigned char) '\0';
+       b->slen = 0;
+       return BSTR_OK;
+}
+
+/*
+ * int brtrimws (bstring b)
+ *
+ * Delete whitespace contiguous from the right end of the string.
+ */
+int brtrimws (bstring b) {
+int i;
+
+       if (b == NULL || b->data == NULL || b->mlen < b->slen ||
+           b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+
+       for (i = b->slen - 1; i >= 0; i--) {
+               if (!wspace (b->data[i])) {
+                       if (b->mlen > i) b->data[i+1] = (unsigned char) '\0';
+                       b->slen = i + 1;
+                       return BSTR_OK;
+               }
+       }
+
+       b->data[0] = (unsigned char) '\0';
+       b->slen = 0;
+       return BSTR_OK;
+}
+
+/*
+ * int btrimws (bstring b)
+ *
+ * Delete whitespace contiguous from both ends of the string.
+ */
+int btrimws (bstring b) {
+int i, j;
+
+       if (b == NULL || b->data == NULL || b->mlen < b->slen ||
+           b->slen < 0 || b->mlen <= 0) return BSTR_ERR;
+
+       for (i = b->slen - 1; i >= 0; i--) {
+               if (!wspace (b->data[i])) {
+                       if (b->mlen > i) b->data[i+1] = (unsigned char) '\0';
+                       b->slen = i + 1;
+                       for (j = 0; wspace (b->data[j]); j++) {}
+                       return bdelete (b, 0, j);
+               }
+       }
+
+       b->data[0] = (unsigned char) '\0';
+       b->slen = 0;
+       return BSTR_OK;
+}
+
+/*  int biseq (const_bstring b0, const_bstring b1)
+ *
+ *  Compare the string b0 and b1.  If the strings differ, 0 is returned, if 
+ *  the strings are the same, 1 is returned, if there is an error, -1 is 
+ *  returned.  If the length of the strings are different, this function is
+ *  O(1).  '\0' termination characters are not treated in any special way.
+ */
+int biseq (const_bstring b0, const_bstring b1) {
+       if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL ||
+               b0->slen < 0 || b1->slen < 0) return BSTR_ERR;
+       if (b0->slen != b1->slen) return BSTR_OK;
+       if (b0->data == b1->data || b0->slen == 0) return 1;
+       return !bstr__memcmp (b0->data, b1->data, b0->slen);
+}
+
+/*  int bisstemeqblk (const_bstring b0, const void * blk, int len)
+ *
+ *  Compare beginning of string b0 with a block of memory of length len for 
+ *  equality.  If the beginning of b0 differs from the memory block (or if b0 
+ *  is too short), 0 is returned, if the strings are the same, 1 is returned, 
+ *  if there is an error, -1 is returned.  '\0' characters are not treated in 
+ *  any special way.
+ */
+int bisstemeqblk (const_bstring b0, const void * blk, int len) {
+int i;
+
+       if (bdata (b0) == NULL || b0->slen < 0 || NULL == blk || len < 0)
+               return BSTR_ERR;
+       if (b0->slen < len) return BSTR_OK;
+       if (b0->data == (const unsigned char *) blk || len == 0) return 1;
+
+       for (i = 0; i < len; i ++) {
+               if (b0->data[i] != ((const unsigned char *) blk)[i]) return BSTR_OK;
+       }
+       return 1;
+}
+
+/*  int biseqcstr (const_bstring b, const char *s)
+ *
+ *  Compare the bstring b and char * string s.  The C string s must be '\0' 
+ *  terminated at exactly the length of the bstring b, and the contents 
+ *  between the two must be identical with the bstring b with no '\0' 
+ *  characters for the two contents to be considered equal.  This is 
+ *  equivalent to the condition that their current contents will be always be 
+ *  equal when comparing them in the same format after converting one or the 
+ *  other.  If the strings are equal 1 is returned, if they are unequal 0 is 
+ *  returned and if there is a detectable error BSTR_ERR is returned.
+ */
+int biseqcstr (const_bstring b, const char * s) {
+int i;
+       if (b == NULL || s == NULL || b->data == NULL || b->slen < 0) return BSTR_ERR;
+       for (i=0; i < b->slen; i++) {
+               if (s[i] == '\0' || b->data[i] != (unsigned char) s[i]) return BSTR_OK;
+       }
+       return s[i] == '\0';
+}
+
+/*  int biseqcstrcaseless (const_bstring b, const char *s)
+ *
+ *  Compare the bstring b and char * string s.  The C string s must be '\0' 
+ *  terminated at exactly the length of the bstring b, and the contents 
+ *  between the two must be identical except for case with the bstring b with 
+ *  no '\0' characters for the two contents to be considered equal.  This is 
+ *  equivalent to the condition that their current contents will be always be 
+ *  equal ignoring case when comparing them in the same format after 
+ *  converting one or the other.  If the strings are equal, except for case, 
+ *  1 is returned, if they are unequal regardless of case 0 is returned and 
+ *  if there is a detectable error BSTR_ERR is returned.
+ */
+int biseqcstrcaseless (const_bstring b, const char * s) {
+int i;
+       if (b == NULL || s == NULL || b->data == NULL || b->slen < 0) return BSTR_ERR;
+       for (i=0; i < b->slen; i++) {
+               if (s[i] == '\0' || 
+                   (b->data[i] != (unsigned char) s[i] && 
+                    downcase (b->data[i]) != (unsigned char) downcase (s[i])))
+                       return BSTR_OK;
+       }
+       return s[i] == '\0';
+}
+
+/*  int bstrcmp (const_bstring b0, const_bstring b1)
+ *
+ *  Compare the string b0 and b1.  If there is an error, SHRT_MIN is returned, 
+ *  otherwise a value less than or greater than zero, indicating that the 
+ *  string pointed to by b0 is lexicographically less than or greater than 
+ *  the string pointed to by b1 is returned.  If the the string lengths are 
+ *  unequal but the characters up until the length of the shorter are equal 
+ *  then a value less than, or greater than zero, indicating that the string 
+ *  pointed to by b0 is shorter or longer than the string pointed to by b1 is 
+ *  returned.  0 is returned if and only if the two strings are the same.  If 
+ *  the length of the strings are different, this function is O(n).  Like its
+ *  standard C library counter part strcmp, the comparison does not proceed 
+ *  past any '\0' termination characters encountered.
+ */
+int bstrcmp (const_bstring b0, const_bstring b1) {
+int i, v, n;
+
+       if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL ||
+               b0->slen < 0 || b1->slen < 0) return SHRT_MIN;
+       n = b0->slen; if (n > b1->slen) n = b1->slen;
+       if (b0->slen == b1->slen && (b0->data == b1->data || b0->slen == 0))
+               return BSTR_OK;
+
+       for (i = 0; i < n; i ++) {
+               v = ((char) b0->data[i]) - ((char) b1->data[i]);
+               if (v != 0) return v;
+               if (b0->data[i] == (unsigned char) '\0') return BSTR_OK;
+       }
+
+       if (b0->slen > n) return 1;
+       if (b1->slen > n) return -1;
+       return BSTR_OK;
+}
+
+/*  int bstrncmp (const_bstring b0, const_bstring b1, int n)
+ *
+ *  Compare the string b0 and b1 for at most n characters.  If there is an 
+ *  error, SHRT_MIN is returned, otherwise a value is returned as if b0 and 
+ *  b1 were first truncated to at most n characters then bstrcmp was called
+ *  with these new strings are paremeters.  If the length of the strings are 
+ *  different, this function is O(n).  Like its standard C library counter 
+ *  part strcmp, the comparison does not proceed past any '\0' termination 
+ *  characters encountered.
+ */
+int bstrncmp (const_bstring b0, const_bstring b1, int n) {
+int i, v, m;
+
+       if (b0 == NULL || b1 == NULL || b0->data == NULL || b1->data == NULL ||
+               b0->slen < 0 || b1->slen < 0) return SHRT_MIN;
+       m = n;
+       if (m > b0->slen) m = b0->slen;
+       if (m > b1->slen) m = b1->slen;
+
+       if (b0->data != b1->data) {
+               for (i = 0; i < m; i ++) {
+                       v = ((char) b0->data[i]) - ((char) b1->data[i]);
+                       if (v != 0) return v;
+                       if (b0->data[i] == (unsigned char) '\0') return BSTR_OK;
+               }
+       }
+
+       if (n == m || b0->slen == b1->slen) return BSTR_OK;
+
+       if (b0->slen > m) return 1;
+       return -1;
+}
+
+/*  bstring bmidstr (const_bstring b, int left, int len)
+ *
+ *  Create a bstring which is the substring of b starting from position left
+ *  and running for a length len (clamped by the end of the bstring b.)  If
+ *  b is detectably invalid, then NULL is returned.  The section described 
+ *  by (left, len) is clamped to the boundaries of b.
+ */
+bstring bmidstr (const_bstring b, int left, int len) {
+
+       if (b == NULL || b->slen < 0 || b->data == NULL) return NULL;
+
+       if (left < 0) {
+               len += left;
+               left = 0;
+       }
+
+       if (len > b->slen - left) len = b->slen - left;
+
+       if (len <= 0) return bfromcstr ("");
+       return blk2bstr (b->data + left, len);
+}
+
+/*  int bdelete (bstring b, int pos, int len)
+ *
+ *  Removes characters from pos to pos+len-1 inclusive and shifts the tail of 
+ *  the bstring starting from pos+len to pos.  len must be positive for this 
+ *  call to have any effect.  The section of the string described by (pos, 
+ *  len) is clamped to boundaries of the bstring b.
+ */
+int bdelete (bstring b, int pos, int len) {
+       /* Clamp to left side of bstring */
+       if (pos < 0) {
+               len += pos;
+               pos = 0;
+       }
+
+       if (len < 0 || b == NULL || b->data == NULL || b->slen < 0 || 
+           b->mlen < b->slen || b->mlen <= 0) 
+               return BSTR_ERR;
+       if (len > 0 && pos < b->slen) {
+               if (pos + len >= b->slen) {
+                       b->slen = pos;
+               } else {
+                       bBlockCopy ((char *) (b->data + pos),
+                                   (char *) (b->data + pos + len), 
+                                   b->slen - (pos+len));
+                       b->slen -= len;
+               }
+               b->data[b->slen] = (unsigned char) '\0';
+       }
+       return BSTR_OK;
+}
+
+/*  int bdestroy (bstring b)
+ *
+ *  Free up the bstring.  Note that if b is detectably invalid or not writable
+ *  then no action is performed and BSTR_ERR is returned.  Like a freed memory
+ *  allocation, dereferences, writes or any other action on b after it has 
+ *  been bdestroyed is undefined.
+ */
+int bdestroy (bstring b) {
+       if (b == NULL || b->slen < 0 || b->mlen <= 0 || b->mlen < b->slen ||
+           b->data == NULL)
+               return BSTR_ERR;
+
+       bstr__free (b->data);
+
+       /* In case there is any stale usage, there is one more chance to 
+          notice this error. */
+
+       b->slen = -1;
+       b->mlen = -__LINE__;
+       b->data = NULL;
+
+       bstr__free (b);
+       return BSTR_OK;
+}
+
+/*  int binstr (const_bstring b1, int pos, const_bstring b2)
+ *
+ *  Search for the bstring b2 in b1 starting from position pos, and searching 
+ *  forward.  If it is found then return with the first position where it is 
+ *  found, otherwise return BSTR_ERR.  Note that this is just a brute force 
+ *  string searcher that does not attempt clever things like the Boyer-Moore 
+ *  search algorithm.  Because of this there are many degenerate cases where 
+ *  this can take much longer than it needs to.
+ */
+int binstr (const_bstring b1, int pos, const_bstring b2) {
+int j, ii, ll, lf;
+unsigned char * d0;
+unsigned char c0;
+register unsigned char * d1;
+register unsigned char c1;
+register int i;
+
+       if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
+           b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR;
+       if (b1->slen == pos) return (b2->slen == 0)?pos:BSTR_ERR;
+       if (b1->slen < pos || pos < 0) return BSTR_ERR;
+       if (b2->slen == 0) return pos;
+
+       /* No space to find such a string? */
+       if ((lf = b1->slen - b2->slen + 1) <= pos) return BSTR_ERR;
+
+       /* An obvious alias case */
+       if (b1->data == b2->data && pos == 0) return 0;
+
+       i = pos;
+
+       d0 = b2->data;
+       d1 = b1->data;
+       ll = b2->slen;
+
+       /* Peel off the b2->slen == 1 case */
+       c0 = d0[0];
+       if (1 == ll) {
+               for (;i < lf; i++) if (c0 == d1[i]) return i;
+               return BSTR_ERR;
+       }
+
+       c1 = c0;
+       j = 0;
+       lf = b1->slen - 1;
+
+       ii = -1;
+       if (i < lf) do {
+               /* Unrolled current character test */
+               if (c1 != d1[i]) {
+                       if (c1 != d1[1+i]) {
+                               i += 2;
+                               continue;
+                       }
+                       i++;
+               }
+
+               /* Take note if this is the start of a potential match */
+               if (0 == j) ii = i;
+
+               /* Shift the test character down by one */
+               j++;
+               i++;
+
+               /* If this isn't past the last character continue */
+               if (j < ll) {
+                       c1 = d0[j];
+                       continue;
+               }
+
+               N0:;
+
+               /* If no characters mismatched, then we matched */
+               if (i == ii+j) return ii;
+
+               /* Shift back to the beginning */
+               i -= j;
+               j  = 0;
+               c1 = c0;
+       } while (i < lf);
+
+       /* Deal with last case if unrolling caused a misalignment */
+       if (i == lf && ll == j+1 && c1 == d1[i]) goto N0;
+
+       return BSTR_ERR;
+}
+
+/*  int binstrr (const_bstring b1, int pos, const_bstring b2)
+ *
+ *  Search for the bstring b2 in b1 starting from position pos, and searching 
+ *  backward.  If it is found then return with the first position where it is 
+ *  found, otherwise return BSTR_ERR.  Note that this is just a brute force 
+ *  string searcher that does not attempt clever things like the Boyer-Moore 
+ *  search algorithm.  Because of this there are many degenerate cases where 
+ *  this can take much longer than it needs to.
+ */
+int binstrr (const_bstring b1, int pos, const_bstring b2) {
+int j, i, l;
+unsigned char * d0, * d1;
+
+       if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
+           b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR;
+       if (b1->slen == pos && b2->slen == 0) return pos;
+       if (b1->slen < pos || pos < 0) return BSTR_ERR;
+       if (b2->slen == 0) return pos;
+
+       /* Obvious alias case */
+       if (b1->data == b2->data && pos == 0 && b2->slen <= b1->slen) return 0;
+
+       i = pos;
+       if ((l = b1->slen - b2->slen) < 0) return BSTR_ERR;
+
+       /* If no space to find such a string then snap back */
+       if (l + 1 <= i) i = l;
+       j = 0;
+
+       d0 = b2->data;
+       d1 = b1->data;
+       l  = b2->slen;
+
+       for (;;) {
+               if (d0[j] == d1[i + j]) {
+                       j ++;
+                       if (j >= l) return i;
+               } else {
+                       i --;
+                       if (i < 0) break;
+                       j=0;
+               }
+       }
+
+       return BSTR_ERR;
+}
+
+/*  int binstrcaseless (const_bstring b1, int pos, const_bstring b2)
+ *
+ *  Search for the bstring b2 in b1 starting from position pos, and searching 
+ *  forward but without regard to case.  If it is found then return with the 
+ *  first position where it is found, otherwise return BSTR_ERR.  Note that 
+ *  this is just a brute force string searcher that does not attempt clever 
+ *  things like the Boyer-Moore search algorithm.  Because of this there are 
+ *  many degenerate cases where this can take much longer than it needs to.
+ */
+int binstrcaseless (const_bstring b1, int pos, const_bstring b2) {
+int j, i, l, ll;
+unsigned char * d0, * d1;
+
+       if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
+           b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR;
+       if (b1->slen == pos) return (b2->slen == 0)?pos:BSTR_ERR;
+       if (b1->slen < pos || pos < 0) return BSTR_ERR;
+       if (b2->slen == 0) return pos;
+
+       l = b1->slen - b2->slen + 1;
+
+       /* No space to find such a string? */
+       if (l <= pos) return BSTR_ERR;
+
+       /* An obvious alias case */
+       if (b1->data == b2->data && pos == 0) return BSTR_OK;
+
+       i = pos;
+       j = 0;
+
+       d0 = b2->data;
+       d1 = b1->data;
+       ll = b2->slen;
+
+       for (;;) {
+               if (d0[j] == d1[i + j] || downcase (d0[j]) == downcase (d1[i + j])) {
+                       j ++;
+                       if (j >= ll) return i;
+               } else {
+                       i ++;
+                       if (i >= l) break;
+                       j=0;
+               }
+       }
+
+       return BSTR_ERR;
+}
+
+/*  int binstrrcaseless (const_bstring b1, int pos, const_bstring b2)
+ *
+ *  Search for the bstring b2 in b1 starting from position pos, and searching 
+ *  backward but without regard to case.  If it is found then return with the 
+ *  first position where it is found, otherwise return BSTR_ERR.  Note that 
+ *  this is just a brute force string searcher that does not attempt clever 
+ *  things like the Boyer-Moore search algorithm.  Because of this there are 
+ *  many degenerate cases where this can take much longer than it needs to.
+ */
+int binstrrcaseless (const_bstring b1, int pos, const_bstring b2) {
+int j, i, l;
+unsigned char * d0, * d1;
+
+       if (b1 == NULL || b1->data == NULL || b1->slen < 0 ||
+           b2 == NULL || b2->data == NULL || b2->slen < 0) return BSTR_ERR;
+       if (b1->slen == pos && b2->slen == 0) return pos;
+       if (b1->slen < pos || pos < 0) return BSTR_ERR;
+       if (b2->slen == 0) return pos;
+
+       /* Obvious alias case */
+       if (b1->data == b2->data && pos == 0 && b2->slen <= b1->slen) return BSTR_OK;
+
+       i = pos;
+       if ((l = b1->slen - b2->slen) < 0) return BSTR_ERR;
+
+       /* If no space to find such a string then snap back */
+       if (l + 1 <= i) i = l;
+       j = 0;
+
+       d0 = b2->data;
+       d1 = b1->data;
+       l  = b2->slen;
+
+       for (;;) {
+               if (d0[j] == d1[i + j] || downcase (d0[j]) == downcase (d1[i + j])) {
+                       j ++;
+                       if (j >= l) return i;
+               } else {
+                       i --;
+                       if (i < 0) break;
+                       j=0;
+               }
+       }
+
+       return BSTR_ERR;
+}
+
+
+/*  int bstrchrp (const_bstring b, int c, int pos)
+ *
+ *  Search for the character c in b forwards from the position pos 
+ *  (inclusive).
+ */
+int bstrchrp (const_bstring b, int c, int pos) {
+unsigned char * p;
+
+       if (b == NULL || b->data == NULL || b->slen <= pos || pos < 0) return BSTR_ERR;
+       p = (unsigned char *) bstr__memchr ((b->data + pos), (unsigned char) c, (b->slen - pos));
+       if (p) return (int) (p - b->data);
+       return BSTR_ERR;
+}
+
+/*  int bstrrchrp (const_bstring b, int c, int pos)
+ *
+ *  Search for the character c in b backwards from the position pos in string 
+ *  (inclusive).
+ */
+int bstrrchrp (const_bstring b, int c, int pos) {
+int i;
+       if (b == NULL || b->data == NULL || b->slen <= pos || pos < 0) return BSTR_ERR;
+       for (i=pos; i >= 0; i--) {
+               if (b->data[i] == (unsigned char) c) return i;
+       }
+       return BSTR_ERR;
+}
+
+#if !defined (BSTRLIB_AGGRESSIVE_MEMORY_FOR_SPEED_TRADEOFF)
+#define LONG_LOG_BITS_QTY (3)
+#define LONG_BITS_QTY (1 << LONG_LOG_BITS_QTY)
+#define LONG_TYPE unsigned char
+
+#define CFCLEN ((1 << CHAR_BIT) / LONG_BITS_QTY)
+struct charField { LONG_TYPE content[CFCLEN]; };
+#define testInCharField(cf,c) ((cf)->content[(c) >> LONG_LOG_BITS_QTY] & (((long)1) << ((c) & (LONG_BITS_QTY-1))))
+#define setInCharField(cf,idx) { \
+       unsigned int c = (unsigned int) (idx); \
+       (cf)->content[c >> LONG_LOG_BITS_QTY] |= (LONG_TYPE) (1ul << (c & (LONG_BITS_QTY-1))); \
+}
+
+#else
+
+#define CFCLEN (1 << CHAR_BIT)
+struct charField { unsigned char content[CFCLEN]; };
+#define testInCharField(cf,c) ((cf)->content[(unsigned char) (c)])
+#define setInCharField(cf,idx) (cf)->content[(unsigned int) (idx)] = ~0
+
+#endif
+
+/* Convert a bstring to charField */
+static int buildCharField (struct charField * cf, const_bstring b) {
+int i;
+       if (b == NULL || b->data == NULL || b->slen <= 0) return BSTR_ERR;
+       memset ((void *) cf->content, 0, sizeof (struct charField));
+       for (i=0; i < b->slen; i++) {
+               setInCharField (cf, b->data[i]);
+       }
+       return BSTR_OK;
+}
+
+static void invertCharField (struct charField * cf) {
+int i;
+       for (i=0; i < CFCLEN; i++) cf->content[i] = ~cf->content[i];
+}
+
+/* Inner engine for binchr */
+static int binchrCF (const unsigned char * data, int len, int pos, const struct charField * cf) {
+int i;
+       for (i=pos; i < len; i++) {
+               unsigned char c = (unsigned char) data[i];
+               if (testInCharField (cf, c)) return i;
+       }
+       return BSTR_ERR;
+}
+
+/*  int binchr (const_bstring b0, int pos, const_bstring b1);
+ *
+ *  Search for the first position in b0 starting from pos or after, in which 
+ *  one of the characters in b1 is found and return it.  If such a position 
+ *  does not exist in b0, then BSTR_ERR is returned.
+ */
+int binchr (const_bstring b0, int pos, const_bstring b1) {
+struct charField chrs;
+       if (pos < 0 || b0 == NULL || b0->data == NULL ||
+           b0->slen <= pos) return BSTR_ERR;
+       if (1 == b1->slen) return bstrchrp (b0, b1->data[0], pos);
+       if (0 > buildCharField (&chrs, b1)) return BSTR_ERR;
+       return binchrCF (b0->data, b0->slen, pos, &chrs);
+}
+
+/* Inner engine for binchrr */
+static int binchrrCF (const unsigned char * data, int pos, const struct charField * cf) {
+int i;
+       for (i=pos; i >= 0; i--) {
+               unsigned int c = (unsigned int) data[i];
+               if (testInCharField (cf, c)) return i;
+       }
+       return BSTR_ERR;
+}
+
+/*  int binchrr (const_bstring b0, int pos, const_bstring b1);
+ *
+ *  Search for the last position in b0 no greater than pos, in which one of 
+ *  the characters in b1 is found and return it.  If such a position does not 
+ *  exist in b0, then BSTR_ERR is returned.
+ */
+int binchrr (const_bstring b0, int pos, const_bstring b1) {
+struct charField chrs;
+       if (pos < 0 || b0 == NULL || b0->data == NULL || b1 == NULL ||
+           b0->slen < pos) return BSTR_ERR;
+       if (pos == b0->slen) pos--;
+       if (1 == b1->slen) return bstrrchrp (b0, b1->data[0], pos);
+       if (0 > buildCharField (&chrs, b1)) return BSTR_ERR;
+       return binchrrCF (b0->data, pos, &chrs);
+}
+
+/*  int bninchr (const_bstring b0, int pos, const_bstring b1);
+ *
+ *  Search for the first position in b0 starting from pos or after, in which 
+ *  none of the characters in b1 is found and return it.  If such a position 
+ *  does not exist in b0, then BSTR_ERR is returned.
+ */
+int bninchr (const_bstring b0, int pos, const_bstring b1) {
+struct charField chrs;
+       if (pos < 0 || b0 == NULL || b0->data == NULL || 
+           b0->slen <= pos) return BSTR_ERR;
+       if (buildCharField (&chrs, b1) < 0) return BSTR_ERR;
+       invertCharField (&chrs);
+       return binchrCF (b0->data, b0->slen, pos, &chrs);
+}
+
+/*  int bninchrr (const_bstring b0, int pos, const_bstring b1);
+ *
+ *  Search for the last position in b0 no greater than pos, in which none of 
+ *  the characters in b1 is found and return it.  If such a position does not 
+ *  exist in b0, then BSTR_ERR is returned.
+ */
+int bninchrr (const_bstring b0, int pos, const_bstring b1) {
+struct charField chrs;
+       if (pos < 0 || b0 == NULL || b0->data == NULL || 
+           b0->slen < pos) return BSTR_ERR;
+       if (pos == b0->slen) pos--;
+       if (buildCharField (&chrs, b1) < 0) return BSTR_ERR;
+       invertCharField (&chrs);
+       return binchrrCF (b0->data, pos, &chrs);
+}
+
+/*  int bsetstr (bstring b0, int pos, bstring b1, unsigned char fill)
+ *
+ *  Overwrite the string b0 starting at position pos with the string b1. If 
+ *  the position pos is past the end of b0, then the character "fill" is 
+ *  appended as necessary to make up the gap between the end of b0 and pos.
+ *  If b1 is NULL, it behaves as if it were a 0-length string.
+ */
+int bsetstr (bstring b0, int pos, const_bstring b1, unsigned char fill) {
+int d, newlen;
+ptrdiff_t pd;
+bstring aux = (bstring) b1;
+
+       if (pos < 0 || b0 == NULL || b0->slen < 0 || NULL == b0->data || 
+           b0->mlen < b0->slen || b0->mlen <= 0) return BSTR_ERR;
+       if (b1 != NULL && (b1->slen < 0 || b1->data == NULL)) return BSTR_ERR;
+
+       d = pos;
+
+       /* Aliasing case */
+       if (NULL != aux) {
+               if ((pd = (ptrdiff_t) (b1->data - b0->data)) >= 0 && pd < (ptrdiff_t) b0->mlen) {
+                       if (NULL == (aux = bstrcpy (b1))) return BSTR_ERR;
+               }
+               d += aux->slen;
+       }
+
+       /* Increase memory size if necessary */
+       if (balloc (b0, d + 1) != BSTR_OK) {
+               if (aux != b1) bdestroy (aux);
+               return BSTR_ERR;
+       }
+
+       newlen = b0->slen;
+
+       /* Fill in "fill" character as necessary */
+       if (pos > newlen) {
+               bstr__memset (b0->data + b0->slen, (int) fill, (size_t) (pos - b0->slen));
+               newlen = pos;
+       }
+
+       /* Copy b1 to position pos in b0. */
+       if (aux != NULL) {
+               bBlockCopy ((char *) (b0->data + pos), (char *) aux->data, aux->slen);
+               if (aux != b1) bdestroy (aux);
+       }
+
+       /* Indicate the potentially increased size of b0 */
+       if (d > newlen) newlen = d;
+
+       b0->slen = newlen;
+       b0->data[newlen] = (unsigned char) '\0';
+
+       return BSTR_OK;
+}
+
+/*  int binsert (bstring b1, int pos, bstring b2, unsigned char fill)
+ *
+ *  Inserts the string b2 into b1 at position pos.  If the position pos is 
+ *  past the end of b1, then the character "fill" is appended as necessary to 
+ *  make up the gap between the end of b1 and pos.  Unlike bsetstr, binsert
+ *  does not allow b2 to be NULL.
+ */
+int binsert (bstring b1, int pos, const_bstring b2, unsigned char fill) {
+int d, l;
+ptrdiff_t pd;
+bstring aux = (bstring) b2;
+
+       if (pos < 0 || b1 == NULL || b2 == NULL || b1->slen < 0 || 
+           b2->slen < 0 || b1->mlen < b1->slen || b1->mlen <= 0) return BSTR_ERR;
+
+       /* Aliasing case */
+       if ((pd = (ptrdiff_t) (b2->data - b1->data)) >= 0 && pd < (ptrdiff_t) b1->mlen) {
+               if (NULL == (aux = bstrcpy (b2))) return BSTR_ERR;
+       }
+
+       /* Compute the two possible end pointers */
+       d = b1->slen + aux->slen;
+       l = pos + aux->slen;
+       if ((d|l) < 0) return BSTR_ERR;
+
+       if (l > d) {
+               /* Inserting past the end of the string */
+               if (balloc (b1, l + 1) != BSTR_OK) {
+                       if (aux != b2) bdestroy (aux);
+                       return BSTR_ERR;
+               }
+               bstr__memset (b1->data + b1->slen, (int) fill, (size_t) (pos - b1->slen));
+               b1->slen = l;
+       } else {
+               /* Inserting in the middle of the string */
+               if (balloc (b1, d + 1) != BSTR_OK) {
+                       if (aux != b2) bdestroy (aux);
+                       return BSTR_ERR;
+               }
+               bBlockCopy (b1->data + l, b1->data + pos, d - l);
+               b1->slen = d;
+       }
+       bBlockCopy (b1->data + pos, aux->data, aux->slen);
+       b1->data[b1->slen] = (unsigned char) '\0';
+       if (aux != b2) bdestroy (aux);
+       return BSTR_OK;
+}
+
+/*  int breplace (bstring b1, int pos, int len, bstring b2, 
+ *                unsigned char fill)
+ *
+ *  Replace a section of a string from pos for a length len with the string b2.
+ *  fill is used is pos > b1->slen.
+ */
+int breplace (bstring b1, int pos, int len, const_bstring b2, 
+                         unsigned char fill) {
+int pl, ret;
+ptrdiff_t pd;
+bstring aux = (bstring) b2;
+
+       if (pos < 0 || len < 0 || (pl = pos + len) < 0 || b1 == NULL || 
+           b2 == NULL || b1->data == NULL || b2->data == NULL || 
+           b1->slen < 0 || b2->slen < 0 || b1->mlen < b1->slen ||
+           b1->mlen <= 0) return BSTR_ERR;
+
+       /* Straddles the end? */
+       if (pl >= b1->slen) {
+               if ((ret = bsetstr (b1, pos, b2, fill)) < 0) return ret;
+               if (pos + b2->slen < b1->slen) {
+                       b1->slen = pos + b2->slen;
+                       b1->data[b1->slen] = (unsigned char) '\0';
+               }
+               return ret;
+       }
+
+       /* Aliasing case */
+       if ((pd = (ptrdiff_t) (b2->data - b1->data)) >= 0 && pd < (ptrdiff_t) b1->slen) {
+               if (NULL == (aux = bstrcpy (b2))) return BSTR_ERR;
+       }
+
+       if (aux->slen > len) {
+               if (balloc (b1, b1->slen + aux->slen - len) != BSTR_OK) {
+                       if (aux != b2) bdestroy (aux);
+                       return BSTR_ERR;
+               }
+       }
+
+       if (aux->slen != len) bstr__memmove (b1->data + pos + aux->slen, b1->data + pos + len, b1->slen - (pos + len));
+       bstr__memcpy (b1->data + pos, aux->data, aux->slen);
+       b1->slen += aux->slen - len;
+       b1->data[b1->slen] = (unsigned char) '\0';
+       if (aux != b2) bdestroy (aux);
+       return BSTR_OK;
+}
+
+/*  int bfindreplace (bstring b, const_bstring find, const_bstring repl, 
+ *                    int pos)
+ *
+ *  Replace all occurrences of a find string with a replace string after a
+ *  given point in a bstring.
+ */
+
+typedef int (*instr_fnptr) (const_bstring s1, int pos, const_bstring s2);
+
+static int findreplaceengine (bstring b, const_bstring find, const_bstring repl, int pos, instr_fnptr instr) {
+int i, ret, slen, mlen, delta, acc;
+int * d;
+int static_d[32];
+ptrdiff_t pd;
+bstring auxf = (bstring) find;
+bstring auxr = (bstring) repl;
+
+       if (b == NULL || b->data == NULL || find == NULL ||
+           find->data == NULL || repl == NULL || repl->data == NULL || 
+           pos < 0 || find->slen <= 0 || b->mlen < 0 || b->slen > b->mlen || 
+           b->mlen <= 0 || b->slen < 0 || repl->slen < 0) return BSTR_ERR;
+       if (pos > b->slen - find->slen) return BSTR_OK;
+
+       /* Alias with find string */
+       pd = (ptrdiff_t) (find->data - b->data);
+       if ((ptrdiff_t) (pos - find->slen) < pd && pd < (ptrdiff_t) b->slen) {
+               if (NULL == (auxf = bstrcpy (find))) return BSTR_ERR;
+       }
+
+       /* Alias with repl string */
+       pd = (ptrdiff_t) (repl->data - b->data);
+       if ((ptrdiff_t) (pos - repl->slen) < pd && pd < (ptrdiff_t) b->slen) {
+               if (NULL == (auxr = bstrcpy (repl))) {
+                       if (auxf != find) bdestroy (auxf);
+                       return BSTR_ERR;
+               }
+       }
+
+       delta = auxf->slen - auxr->slen;
+
+       /* in-place replacement since find and replace strings are of equal 
+          length */
+       if (delta == 0) {
+               while ((pos = instr (b, pos, auxf)) >= 0) {
+                       bstr__memcpy (b->data + pos, auxr->data, auxr->slen);
+                       pos += auxf->slen;
+               }
+               if (auxf != find) bdestroy (auxf);
+               if (auxr != repl) bdestroy (auxr);
+               return BSTR_OK;
+       }
+
+       /* shrinking replacement since auxf->slen > auxr->slen */
+       if (delta > 0) {
+               acc = 0;
+
+               while ((i = instr (b, pos, auxf)) >= 0) {
+                       if (acc && i > pos)
+                               bstr__memmove (b->data + pos - acc, b->data + pos, i - pos);
+                       if (auxr->slen)
+                               bstr__memcpy (b->data + i - acc, auxr->data, auxr->slen);
+                       acc += delta;
+                       pos = i + auxf->slen;
+               }
+
+               if (acc) {
+                       i = b->slen;
+                       if (i > pos)
+                               bstr__memmove (b->data + pos - acc, b->data + pos, i - pos);
+                       b->slen -= acc;
+                       b->data[b->slen] = (unsigned char) '\0';
+               }
+
+               if (auxf != find) bdestroy (auxf);
+               if (auxr != repl) bdestroy (auxr);
+               return BSTR_OK;
+       }
+
+       /* expanding replacement since find->slen < repl->slen.  Its a lot 
+          more complicated. */
+
+       mlen = 32;
+       d = (int *) static_d; /* Avoid malloc for trivial cases */
+       acc = slen = 0;
+
+       while ((pos = instr (b, pos, auxf)) >= 0) {
+               if (slen + 1 >= mlen) {
+                       int sl;
+                       int * t;
+                       mlen += mlen;
+                       sl = sizeof (int *) * mlen;
+                       if (static_d == d) d = NULL;
+                       if (sl < mlen || NULL == (t = (int *) bstr__realloc (d, sl))) {
+                               ret = BSTR_ERR;
+                               goto done;
+                       }
+                       if (NULL == d) bstr__memcpy (t, static_d, sizeof (static_d));
+                       d = t;
+               }
+               d[slen] = pos;
+               slen++;
+               acc -= delta;
+               pos += auxf->slen;
+               if (pos < 0 || acc < 0) {
+                       ret = BSTR_ERR;
+                       goto done;
+               }
+       }
+       d[slen] = b->slen;
+
+       if (BSTR_OK == (ret = balloc (b, b->slen + acc + 1))) {
+               b->slen += acc;
+               for (i = slen-1; i >= 0; i--) {
+                       int s, l;
+                       s = d[i] + auxf->slen;
+                       l = d[i+1] - s;
+                       if (l) {
+                               bstr__memmove (b->data + s + acc, b->data + s, l);
+                       }
+                       if (auxr->slen) {
+                               bstr__memmove (b->data + s + acc - auxr->slen, 
+                                        auxr->data, auxr->slen);
+                       }
+                       acc += delta;           
+               }
+               b->data[b->slen] = (unsigned char) '\0';
+       }
+
+       done:;
+       if (static_d == d) d = NULL;
+       bstr__free (d);
+       if (auxf != find) bdestroy (auxf);
+       if (auxr != repl) bdestroy (auxr);
+       return ret;
+}
+
+/*  int bfindreplace (bstring b, const_bstring find, const_bstring repl, 
+ *                    int pos)
+ *
+ *  Replace all occurrences of a find string with a replace string after a
+ *  given point in a bstring.
+ */
+int bfindreplace (bstring b, const_bstring find, const_bstring repl, int pos) {
+       return findreplaceengine (b, find, repl, pos, binstr);
+}
+
+/*  int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl, 
+ *                    int pos)
+ *
+ *  Replace all occurrences of a find string, ignoring case, with a replace 
+ *  string after a given point in a bstring.
+ */
+int bfindreplacecaseless (bstring b, const_bstring find, const_bstring repl, int pos) {
+       return findreplaceengine (b, find, repl, pos, binstrcaseless);
+}
+
+/*  int binsertch (bstring b, int pos, int len, unsigned char fill)
+ *
+ *  Inserts the character fill repeatedly into b at position pos for a 
+ *  length len.  If the position pos is past the end of b, then the 
+ *  character "fill" is appended as necessary to make up the gap between the 
+ *  end of b and the position pos + len.
+ */
+int binsertch (bstring b, int pos, int len, unsigned char fill) {
+int d, l, i;
+
+       if (pos < 0 || b == NULL || b->slen < 0 || b->mlen < b->slen ||
+           b->mlen <= 0 || len < 0) return BSTR_ERR;
+
+       /* Compute the two possible end pointers */
+       d = b->slen + len;
+       l = pos + len;
+       if ((d|l) < 0) return BSTR_ERR;
+
+       if (l > d) {
+               /* Inserting past the end of the string */
+               if (balloc (b, l + 1) != BSTR_OK) return BSTR_ERR;
+               pos = b->slen;
+               b->slen = l;
+       } else {
+               /* Inserting in the middle of the string */
+               if (balloc (b, d + 1) != BSTR_OK) return BSTR_ERR;
+               for (i = d - 1; i >= l; i--) {
+                       b->data[i] = b->data[i - len];
+               }
+               b->slen = d;
+       }
+
+       for (i=pos; i < l; i++) b->data[i] = fill;
+       b->data[b->slen] = (unsigned char) '\0';
+       return BSTR_OK;
+}
+
+/*  int bpattern (bstring b, int len)
+ *
+ *  Replicate the bstring, b in place, end to end repeatedly until it 
+ *  surpasses len characters, then chop the result to exactly len characters. 
+ *  This function operates in-place.  The function will return with BSTR_ERR 
+ *  if b is NULL or of length 0, otherwise BSTR_OK is returned.
+ */
+int bpattern (bstring b, int len) {
+int i, d;
+
+       d = blength (b);
+       if (d <= 0 || len < 0 || balloc (b, len + 1) != BSTR_OK) return BSTR_ERR;
+       if (len > 0) {
+               if (d == 1) return bsetstr (b, len, NULL, b->data[0]);
+               for (i = d; i < len; i++) b->data[i] = b->data[i - d];
+       }
+       b->data[len] = (unsigned char) '\0';
+       b->slen = len;
+       return BSTR_OK;
+}
+
+#define BS_BUFF_SZ (1024)
+
+/*  int breada (bstring b, bNread readPtr, void * parm)
+ *
+ *  Use a finite buffer fread-like function readPtr to concatenate to the 
+ *  bstring b the entire contents of file-like source data in a roughly 
+ *  efficient way.
+ */
+int breada (bstring b, bNread readPtr, void * parm) {
+int i, l, n;
+
+       if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen ||
+           b->mlen <= 0 || readPtr == NULL) return BSTR_ERR;
+
+       i = b->slen;
+       for (n=i+16; ; n += ((n < BS_BUFF_SZ) ? n : BS_BUFF_SZ)) {
+               if (BSTR_OK != balloc (b, n + 1)) return BSTR_ERR;
+               l = (int) readPtr ((void *) (b->data + i), 1, n - i, parm);
+               i += l;
+               b->slen = i;
+               if (i < n) break;
+       }
+
+       b->data[i] = (unsigned char) '\0';
+       return BSTR_OK;
+}
+
+/*  bstring bread (bNread readPtr, void * parm)
+ *
+ *  Use a finite buffer fread-like function readPtr to create a bstring 
+ *  filled with the entire contents of file-like source data in a roughly 
+ *  efficient way.
+ */
+bstring bread (bNread readPtr, void * parm) {
+bstring buff;
+
+       if (0 > breada (buff = bfromcstr (""), readPtr, parm)) {
+               bdestroy (buff);
+               return NULL;
+       }
+       return buff;
+}
+
+/*  int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator)
+ *
+ *  Use an fgetc-like single character stream reading function (getcPtr) to 
+ *  obtain a sequence of characters which are concatenated to the end of the
+ *  bstring b.  The stream read is terminated by the passed in terminator 
+ *  parameter.
+ *
+ *  If getcPtr returns with a negative number, or the terminator character 
+ *  (which is appended) is read, then the stream reading is halted and the 
+ *  function returns with a partial result in b.  If there is an empty partial
+ *  result, 1 is returned.  If no characters are read, or there is some other 
+ *  detectable error, BSTR_ERR is returned.
+ */
+int bassigngets (bstring b, bNgetc getcPtr, void * parm, char terminator) {
+int c, d, e;
+
+       if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen ||
+           b->mlen <= 0 || getcPtr == NULL) return BSTR_ERR;
+       d = 0;
+       e = b->mlen - 2;
+
+       while ((c = getcPtr (parm)) >= 0) {
+               if (d > e) {
+                       b->slen = d;
+                       if (balloc (b, d + 2) != BSTR_OK) return BSTR_ERR;
+                       e = b->mlen - 2;
+               }
+               b->data[d] = (unsigned char) c;
+               d++;
+               if (c == terminator) break;
+       }
+
+       b->data[d] = (unsigned char) '\0';
+       b->slen = d;
+
+       return d == 0 && c < 0;
+}
+
+/*  int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator)
+ *
+ *  Use an fgetc-like single character stream reading function (getcPtr) to 
+ *  obtain a sequence of characters which are concatenated to the end of the
+ *  bstring b.  The stream read is terminated by the passed in terminator 
+ *  parameter.
+ *
+ *  If getcPtr returns with a negative number, or the terminator character 
+ *  (which is appended) is read, then the stream reading is halted and the 
+ *  function returns with a partial result concatentated to b.  If there is 
+ *  an empty partial result, 1 is returned.  If no characters are read, or 
+ *  there is some other detectable error, BSTR_ERR is returned.
+ */
+int bgetsa (bstring b, bNgetc getcPtr, void * parm, char terminator) {
+int c, d, e;
+
+       if (b == NULL || b->mlen <= 0 || b->slen < 0 || b->mlen < b->slen ||
+           b->mlen <= 0 || getcPtr == NULL) return BSTR_ERR;
+       d = b->slen;
+       e = b->mlen - 2;
+
+       while ((c = getcPtr (parm)) >= 0) {
+               if (d > e) {
+                       b->slen = d;
+                       if (balloc (b, d + 2) != BSTR_OK) return BSTR_ERR;
+                       e = b->mlen - 2;
+               }
+               b->data[d] = (unsigned char) c;
+               d++;
+               if (c == terminator) break;
+       }
+
+       b->data[d] = (unsigned char) '\0';
+       b->slen = d;
+
+       return d == 0 && c < 0;
+}
+
+/*  bstring bgetstream (bNgetc getcPtr, void * parm, char terminator)
+ *
+ *  Use an fgetc-like single character stream reading function (getcPtr) to 
+ *  obtain a sequence of characters which are concatenated into a bstring.  
+ *  The stream read is terminated by the passed in terminator function.
+ *
+ *  If getcPtr returns with a negative number, or the terminator character 
+ *  (which is appended) is read, then the stream reading is halted and the 
+ *  result obtained thus far is returned.  If no characters are read, or 
+ *  there is some other detectable error, NULL is returned.
+ */
+bstring bgetstream (bNgetc getcPtr, void * parm, char terminator) {
+bstring buff;
+
+       if (0 > bgetsa (buff = bfromcstr (""), getcPtr, parm, terminator) || 0 >= buff->slen) {
+               bdestroy (buff);
+               buff = NULL;
+       }
+       return buff;
+}
+
+struct bStream {
+       bstring buff;           /* Buffer for over-reads */
+       void * parm;            /* The stream handle for core stream */
+       bNread readFnPtr;       /* fread compatible fnptr for core stream */
+       int isEOF;              /* track file's EOF state */
+       int maxBuffSz;
+};
+
+/*  struct bStream * bsopen (bNread readPtr, void * parm)
+ *
+ *  Wrap a given open stream (described by a fread compatible function 
+ *  pointer and stream handle) into an open bStream suitable for the bstring 
+ *  library streaming functions.
+ */
+struct bStream * bsopen (bNread readPtr, void * parm) {
+struct bStream * s;
+
+       if (readPtr == NULL) return NULL;
+       s = (struct bStream *) bstr__alloc (sizeof (struct bStream));
+       if (s == NULL) return NULL;
+       s->parm = parm;
+       s->buff = bfromcstr ("");
+       s->readFnPtr = readPtr;
+       s->maxBuffSz = BS_BUFF_SZ;
+       s->isEOF = 0;
+       return s;
+}
+
+/*  int bsbufflength (struct bStream * s, int sz)
+ *
+ *  Set the length of the buffer used by the bStream.  If sz is zero, the 
+ *  length is not set.  This function returns with the previous length.
+ */
+int bsbufflength (struct bStream * s, int sz) {
+int oldSz;
+       if (s == NULL || sz < 0) return BSTR_ERR;
+       oldSz = s->maxBuffSz;
+       if (sz > 0) s->maxBuffSz = sz;
+       return oldSz;
+}
+
+int bseof (const struct bStream * s) {
+       if (s == NULL || s->readFnPtr == NULL) return BSTR_ERR;
+       return s->isEOF && (s->buff->slen == 0);
+}
+
+/*  void * bsclose (struct bStream * s)
+ *
+ *  Close the bStream, and return the handle to the stream that was originally
+ *  used to open the given stream.
+ */
+void * bsclose (struct bStream * s) {
+void * parm;
+       if (s == NULL) return NULL;
+       s->readFnPtr = NULL;
+       if (s->buff) bdestroy (s->buff);
+       s->buff = NULL;
+       parm = s->parm;
+       s->parm = NULL;
+       s->isEOF = 1;
+       bstr__free (s);
+       return parm;
+}
+
+/*  int bsreadlna (bstring r, struct bStream * s, char terminator)
+ *
+ *  Read a bstring terminated by the terminator character or the end of the
+ *  stream from the bStream (s) and return it into the parameter r.  This 
+ *  function may read additional characters from the core stream that are not 
+ *  returned, but will be retained for subsequent read operations.
+ */
+int bsreadlna (bstring r, struct bStream * s, char terminator) {
+int i, l, ret, rlo;
+char * b;
+struct tagbstring x;
+
+       if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0 ||
+           r->slen < 0 || r->mlen < r->slen) return BSTR_ERR;
+       l = s->buff->slen;
+       if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
+       b = (char *) s->buff->data;
+       x.data = (unsigned char *) b;
+
+       /* First check if the current buffer holds the terminator */
+       b[l] = terminator; /* Set sentinel */
+       for (i=0; b[i] != terminator; i++) ;
+       if (i < l) {
+               x.slen = i + 1;
+               ret = bconcat (r, &x);
+               s->buff->slen = l;
+               if (BSTR_OK == ret) bdelete (s->buff, 0, i + 1);
+               return BSTR_OK;
+       }
+
+       rlo = r->slen;
+
+       /* If not then just concatenate the entire buffer to the output */
+       x.slen = l;
+       if (BSTR_OK != bconcat (r, &x)) return BSTR_ERR;
+
+       /* Perform direct in-place reads into the destination to allow for
+          the minimum of data-copies */
+       for (;;) {
+               if (BSTR_OK != balloc (r, r->slen + s->maxBuffSz + 1)) return BSTR_ERR;
+               b = (char *) (r->data + r->slen);
+               l = (int) s->readFnPtr (b, 1, s->maxBuffSz, s->parm);
+               if (l <= 0) {
+                       r->data[r->slen] = (unsigned char) '\0';
+                       s->buff->slen = 0;
+                       s->isEOF = 1;
+                       /* If nothing was read return with an error message */
+                       return BSTR_ERR & -(r->slen == rlo);
+               }
+               b[l] = terminator; /* Set sentinel */
+               for (i=0; b[i] != terminator; i++) ;
+               if (i < l) break;
+               r->slen += l;
+       }
+
+       /* Terminator found, push over-read back to buffer */
+       i++;
+       r->slen += i;
+       s->buff->slen = l - i;
+       bstr__memcpy (s->buff->data, b + i, l - i);
+       r->data[r->slen] = (unsigned char) '\0';
+       return BSTR_OK;
+}
+
+/*  int bsreadlnsa (bstring r, struct bStream * s, bstring term)
+ *
+ *  Read a bstring terminated by any character in the term string or the end 
+ *  of the stream from the bStream (s) and return it into the parameter r.  
+ *  This function may read additional characters from the core stream that 
+ *  are not returned, but will be retained for subsequent read operations.
+ */
+int bsreadlnsa (bstring r, struct bStream * s, const_bstring term) {
+int i, l, ret, rlo;
+unsigned char * b;
+struct tagbstring x;
+struct charField cf;
+
+       if (s == NULL || s->buff == NULL || r == NULL || term == NULL ||
+           term->data == NULL || r->mlen <= 0 || r->slen < 0 ||
+           r->mlen < r->slen) return BSTR_ERR;
+       if (term->slen == 1) return bsreadlna (r, s, term->data[0]);
+       if (term->slen < 1 || buildCharField (&cf, term)) return BSTR_ERR;
+
+       l = s->buff->slen;
+       if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
+       b = (unsigned char *) s->buff->data;
+       x.data = b;
+
+       /* First check if the current buffer holds the terminator */
+       b[l] = term->data[0]; /* Set sentinel */
+       for (i=0; !testInCharField (&cf, b[i]); i++) ;
+       if (i < l) {
+               x.slen = i + 1;
+               ret = bconcat (r, &x);
+               s->buff->slen = l;
+               if (BSTR_OK == ret) bdelete (s->buff, 0, i + 1);
+               return BSTR_OK;
+       }
+
+       rlo = r->slen;
+
+       /* If not then just concatenate the entire buffer to the output */
+       x.slen = l;
+       if (BSTR_OK != bconcat (r, &x)) return BSTR_ERR;
+
+       /* Perform direct in-place reads into the destination to allow for
+          the minimum of data-copies */
+       for (;;) {
+               if (BSTR_OK != balloc (r, r->slen + s->maxBuffSz + 1)) return BSTR_ERR;
+               b = (unsigned char *) (r->data + r->slen);
+               l = (int) s->readFnPtr (b, 1, s->maxBuffSz, s->parm);
+               if (l <= 0) {
+                       r->data[r->slen] = (unsigned char) '\0';
+                       s->buff->slen = 0;
+                       s->isEOF = 1;
+                       /* If nothing was read return with an error message */
+                       return BSTR_ERR & -(r->slen == rlo);
+               }
+
+               b[l] = term->data[0]; /* Set sentinel */
+               for (i=0; !testInCharField (&cf, b[i]); i++) ;
+               if (i < l) break;
+               r->slen += l;
+       }
+
+       /* Terminator found, push over-read back to buffer */
+       i++;
+       r->slen += i;
+       s->buff->slen = l - i;
+       bstr__memcpy (s->buff->data, b + i, l - i);
+       r->data[r->slen] = (unsigned char) '\0';
+       return BSTR_OK;
+}
+
+/*  int bsreada (bstring r, struct bStream * s, int n)
+ *
+ *  Read a bstring of length n (or, if it is fewer, as many bytes as is 
+ *  remaining) from the bStream.  This function may read additional 
+ *  characters from the core stream that are not returned, but will be 
+ *  retained for subsequent read operations.  This function will not read
+ *  additional characters from the core stream beyond virtual stream pointer.
+ */
+int bsreada (bstring r, struct bStream * s, int n) {
+int l, ret, orslen;
+char * b;
+struct tagbstring x;
+
+       if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0
+        || r->slen < 0 || r->mlen < r->slen || n <= 0) return BSTR_ERR;
+
+       n += r->slen;
+       if (n <= 0) return BSTR_ERR;
+
+       l = s->buff->slen;
+
+       orslen = r->slen;
+
+       if (0 == l) {
+               if (s->isEOF) return BSTR_ERR;
+               if (r->mlen > n) {
+                       l = (int) s->readFnPtr (r->data + r->slen, 1, n - r->slen, s->parm);
+                       if (0 >= l || l > n - r->slen) {
+                               s->isEOF = 1;
+                               return BSTR_ERR;
+                       }
+                       r->slen += l;
+                       r->data[r->slen] = (unsigned char) '\0';
+                       return 0;
+               }
+       }
+
+       if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
+       b = (char *) s->buff->data;
+       x.data = (unsigned char *) b;
+
+       do {
+               if (l + r->slen >= n) {
+                       x.slen = n - r->slen;
+                       ret = bconcat (r, &x);
+                       s->buff->slen = l;
+                       if (BSTR_OK == ret) bdelete (s->buff, 0, x.slen);
+                       return BSTR_ERR & -(r->slen == orslen);
+               }
+
+               x.slen = l;
+               if (BSTR_OK != bconcat (r, &x)) break;
+
+               l = n - r->slen;
+               if (l > s->maxBuffSz) l = s->maxBuffSz;
+
+               l = (int) s->readFnPtr (b, 1, l, s->parm);
+
+       } while (l > 0);
+       if (l < 0) l = 0;
+       if (l == 0) s->isEOF = 1;
+       s->buff->slen = l;
+       return BSTR_ERR & -(r->slen == orslen);
+}
+
+/*  int bsreadln (bstring r, struct bStream * s, char terminator)
+ *
+ *  Read a bstring terminated by the terminator character or the end of the
+ *  stream from the bStream (s) and return it into the parameter r.  This 
+ *  function may read additional characters from the core stream that are not 
+ *  returned, but will be retained for subsequent read operations.
+ */
+int bsreadln (bstring r, struct bStream * s, char terminator) {
+       if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0)
+               return BSTR_ERR;
+       if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
+       r->slen = 0;
+       return bsreadlna (r, s, terminator);
+}
+
+/*  int bsreadlns (bstring r, struct bStream * s, bstring term)
+ *
+ *  Read a bstring terminated by any character in the term string or the end 
+ *  of the stream from the bStream (s) and return it into the parameter r.  
+ *  This function may read additional characters from the core stream that 
+ *  are not returned, but will be retained for subsequent read operations.
+ */
+int bsreadlns (bstring r, struct bStream * s, const_bstring term) {
+       if (s == NULL || s->buff == NULL || r == NULL || term == NULL 
+        || term->data == NULL || r->mlen <= 0) return BSTR_ERR;
+       if (term->slen == 1) return bsreadln (r, s, term->data[0]);
+       if (term->slen < 1) return BSTR_ERR;
+       if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
+       r->slen = 0;
+       return bsreadlnsa (r, s, term);
+}
+
+/*  int bsread (bstring r, struct bStream * s, int n)
+ *
+ *  Read a bstring of length n (or, if it is fewer, as many bytes as is 
+ *  remaining) from the bStream.  This function may read additional 
+ *  characters from the core stream that are not returned, but will be 
+ *  retained for subsequent read operations.  This function will not read
+ *  additional characters from the core stream beyond virtual stream pointer.
+ */
+int bsread (bstring r, struct bStream * s, int n) {
+       if (s == NULL || s->buff == NULL || r == NULL || r->mlen <= 0
+        || n <= 0) return BSTR_ERR;
+       if (BSTR_OK != balloc (s->buff, s->maxBuffSz + 1)) return BSTR_ERR;
+       r->slen = 0;
+       return bsreada (r, s, n);
+}
+
+/*  int bsunread (struct bStream * s, const_bstring b)
+ *
+ *  Insert a bstring into the bStream at the current position.  These 
+ *  characters will be read prior to those that actually come from the core 
+ *  stream.
+ */
+int bsunread (struct bStream * s, const_bstring b) {
+       if (s == NULL || s->buff == NULL) return BSTR_ERR;
+       return binsert (s->buff, 0, b, (unsigned char) '?');
+}
+
+/*  int bspeek (bstring r, const struct bStream * s)
+ *
+ *  Return the currently buffered characters from the bStream that will be 
+ *  read prior to reads from the core stream.
+ */
+int bspeek (bstring r, const struct bStream * s) {
+       if (s == NULL || s->buff == NULL) return BSTR_ERR;
+       return bassign (r, s->buff);
+}
+
+/*  bstring bjoin (const struct bstrList * bl, const_bstring sep);
+ *
+ *  Join the entries of a bstrList into one bstring by sequentially 
+ *  concatenating them with the sep string in between.  If there is an error 
+ *  NULL is returned, otherwise a bstring with the correct result is returned.
+ */
+bstring bjoin (const struct bstrList * bl, const_bstring sep) {
+bstring b;
+int i, c, v;
+
+       if (bl == NULL || bl->qty < 0) return NULL;
+       if (sep != NULL && (sep->slen < 0 || sep->data == NULL)) return NULL;
+
+       for (i = 0, c = 1; i < bl->qty; i++) {
+               v = bl->entry[i]->slen;
+               if (v < 0) return NULL; /* Invalid input */
+               c += v;
+               if (c < 0) return NULL; /* Wrap around ?? */
+       }
+
+       if (sep != NULL) c += (bl->qty - 1) * sep->slen;
+
+       b = (bstring) bstr__alloc (sizeof (struct tagbstring));
+       if (NULL == b) return NULL; /* Out of memory */
+       b->data = (unsigned char *) bstr__alloc (c);
+       if (b->data == NULL) {
+               bstr__free (b);
+               return NULL;
+       }
+
+       b->mlen = c;
+       b->slen = c-1;
+
+       for (i = 0, c = 0; i < bl->qty; i++) {
+               if (i > 0 && sep != NULL) {
+                       bstr__memcpy (b->data + c, sep->data, sep->slen);
+                       c += sep->slen;
+               }
+               v = bl->entry[i]->slen;
+               bstr__memcpy (b->data + c, bl->entry[i]->data, v);
+               c += v;
+       }
+       b->data[c] = (unsigned char) '\0';
+       return b;
+}
+
+#define BSSSC_BUFF_LEN (256)
+
+/*  int bssplitscb (struct bStream * s, const_bstring splitStr, 
+ *     int (* cb) (void * parm, int ofs, const_bstring entry), void * parm)
+ *
+ *  Iterate the set of disjoint sequential substrings read from a stream 
+ *  divided by any of the characters in splitStr.  An empty splitStr causes 
+ *  the whole stream to be iterated once.
+ *
+ *  Note: At the point of calling the cb function, the bStream pointer is 
+ *  pointed exactly at the position right after having read the split 
+ *  character.  The cb function can act on the stream by causing the bStream
+ *  pointer to move, and bssplitscb will continue by starting the next split
+ *  at the position of the pointer after the return from cb.
+ *
+ *  However, if the cb causes the bStream s to be destroyed then the cb must
+ *  return with a negative value, otherwise bssplitscb will continue in an 
+ *  undefined manner.
+ */
+int bssplitscb (struct bStream * s, const_bstring splitStr, 
+       int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) {
+struct charField chrs;
+bstring buff;
+int i, p, ret;
+
+       if (cb == NULL || s == NULL || s->readFnPtr == NULL 
+        || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR;
+
+       if (NULL == (buff = bfromcstr (""))) return BSTR_ERR;
+
+       if (splitStr->slen == 0) {
+               while (bsreada (buff, s, BSSSC_BUFF_LEN) >= 0) ;
+               if ((ret = cb (parm, 0, buff)) > 0) 
+                       ret = 0;
+       } else {
+               buildCharField (&chrs, splitStr);
+               ret = p = i = 0;
+               for (;;) {
+                       if (i >= buff->slen) {
+                               bsreada (buff, s, BSSSC_BUFF_LEN);
+                               if (i >= buff->slen) {
+                                       if (0 < (ret = cb (parm, p, buff))) ret = 0;
+                                       break;
+                               }
+                       }
+                       if (testInCharField (&chrs, buff->data[i])) {
+                               struct tagbstring t;
+                               unsigned char c;
+
+                               blk2tbstr (t, buff->data + i + 1, buff->slen - (i + 1));
+                               if ((ret = bsunread (s, &t)) < 0) break;
+                               buff->slen = i;
+                               c = buff->data[i];
+                               buff->data[i] = (unsigned char) '\0';
+                               if ((ret = cb (parm, p, buff)) < 0) break;
+                               buff->data[i] = c;
+                               buff->slen = 0;
+                               p += i + 1;
+                               i = -1;
+                       }
+                       i++;
+               }
+       }
+
+       bdestroy (buff);
+       return ret;
+}
+
+/*  int bssplitstrcb (struct bStream * s, const_bstring splitStr, 
+ *     int (* cb) (void * parm, int ofs, const_bstring entry), void * parm)
+ *
+ *  Iterate the set of disjoint sequential substrings read from a stream 
+ *  divided by the entire substring splitStr.  An empty splitStr causes 
+ *  each character of the stream to be iterated.
+ *
+ *  Note: At the point of calling the cb function, the bStream pointer is 
+ *  pointed exactly at the position right after having read the split 
+ *  character.  The cb function can act on the stream by causing the bStream
+ *  pointer to move, and bssplitscb will continue by starting the next split
+ *  at the position of the pointer after the return from cb.
+ *
+ *  However, if the cb causes the bStream s to be destroyed then the cb must
+ *  return with a negative value, otherwise bssplitscb will continue in an 
+ *  undefined manner.
+ */
+int bssplitstrcb (struct bStream * s, const_bstring splitStr, 
+       int (* cb) (void * parm, int ofs, const_bstring entry), void * parm) {
+bstring buff;
+int i, p, ret;
+
+       if (cb == NULL || s == NULL || s->readFnPtr == NULL 
+        || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR;
+
+       if (splitStr->slen == 1) return bssplitscb (s, splitStr, cb, parm);
+
+       if (NULL == (buff = bfromcstr (""))) return BSTR_ERR;
+
+       if (splitStr->slen == 0) {
+               for (i=0; bsreada (buff, s, BSSSC_BUFF_LEN) >= 0; i++) {
+                       if ((ret = cb (parm, 0, buff)) < 0) {
+                               bdestroy (buff);
+                               return ret;
+                       }
+                       buff->slen = 0;
+               }
+               return BSTR_OK;
+       } else {
+               ret = p = i = 0;
+               for (i=p=0;;) {
+                       if ((ret = binstr (buff, 0, splitStr)) >= 0) {
+                               struct tagbstring t;
+                               blk2tbstr (t, buff->data, ret);
+                               i = ret + splitStr->slen;
+                               if ((ret = cb (parm, p, &t)) < 0) break;
+                               p += i;
+                               bdelete (buff, 0, i);
+                       } else {
+                               bsreada (buff, s, BSSSC_BUFF_LEN);
+                               if (bseof (s)) {
+                                       if ((ret = cb (parm, p, buff)) > 0) ret = 0;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       bdestroy (buff);
+       return ret;
+}
+
+/*  int bstrListCreate (void)
+ *
+ *  Create a bstrList.
+ */
+struct bstrList * bstrListCreate (void) {
+struct bstrList * sl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList));
+       if (sl) {
+               sl->entry = (bstring *) bstr__alloc (1*sizeof (bstring));
+               if (!sl->entry) {
+                       bstr__free (sl);
+                       sl = NULL;
+               } else {
+                       sl->qty = 0;
+                       sl->mlen = 1;
+               }
+       }
+       return sl;
+}
+
+/*  int bstrListDestroy (struct bstrList * sl)
+ *
+ *  Destroy a bstrList that has been created by bsplit, bsplits or bstrListCreate.
+ */
+int bstrListDestroy (struct bstrList * sl) {
+int i;
+       if (sl == NULL || sl->qty < 0) return BSTR_ERR;
+       for (i=0; i < sl->qty; i++) {
+               if (sl->entry[i]) {
+                       bdestroy (sl->entry[i]);
+                       sl->entry[i] = NULL;
+               }
+       }
+       sl->qty  = -1;
+       sl->mlen = -1;
+       bstr__free (sl->entry);
+       sl->entry = NULL;
+       bstr__free (sl);
+       return BSTR_OK;
+}
+
+/*  int bstrListAlloc (struct bstrList * sl, int msz)
+ *
+ *  Ensure that there is memory for at least msz number of entries for the
+ *  list.
+ */
+int bstrListAlloc (struct bstrList * sl, int msz) {
+bstring * l;
+int smsz;
+size_t nsz;
+       if (!sl || msz <= 0 || !sl->entry || sl->qty < 0 || sl->mlen <= 0 || sl->qty > sl->mlen) return BSTR_ERR;
+       if (sl->mlen >= msz) return BSTR_OK;
+       smsz = snapUpSize (msz);
+       nsz = ((size_t) smsz) * sizeof (bstring);
+       if (nsz < (size_t) smsz) return BSTR_ERR;
+       l = (bstring *) bstr__realloc (sl->entry, nsz);
+       if (!l) {
+               smsz = msz;
+               nsz = ((size_t) smsz) * sizeof (bstring);
+               l = (bstring *) bstr__realloc (sl->entry, nsz);
+               if (!l) return BSTR_ERR;
+       }
+       sl->mlen = smsz;
+       sl->entry = l;
+       return BSTR_OK;
+}
+
+/*  int bstrListAllocMin (struct bstrList * sl, int msz)
+ *
+ *  Try to allocate the minimum amount of memory for the list to include at
+ *  least msz entries or sl->qty whichever is greater.
+ */
+int bstrListAllocMin (struct bstrList * sl, int msz) {
+bstring * l;
+size_t nsz;
+       if (!sl || msz <= 0 || !sl->entry || sl->qty < 0 || sl->mlen <= 0 || sl->qty > sl->mlen) return BSTR_ERR;
+       if (msz < sl->qty) msz = sl->qty;
+       if (sl->mlen == msz) return BSTR_OK;
+       nsz = ((size_t) msz) * sizeof (bstring);
+       if (nsz < (size_t) msz) return BSTR_ERR;
+       l = (bstring *) bstr__realloc (sl->entry, nsz);
+       if (!l) return BSTR_ERR;
+       sl->mlen = msz;
+       sl->entry = l;
+       return BSTR_OK;
+}
+
+/*  int bsplitcb (const_bstring str, unsigned char splitChar, int pos,
+ *     int (* cb) (void * parm, int ofs, int len), void * parm)
+ *
+ *  Iterate the set of disjoint sequential substrings over str divided by the
+ *  character in splitChar.
+ *
+ *  Note: Non-destructive modification of str from within the cb function 
+ *  while performing this split is not undefined.  bsplitcb behaves in 
+ *  sequential lock step with calls to cb.  I.e., after returning from a cb 
+ *  that return a non-negative integer, bsplitcb continues from the position 
+ *  1 character after the last detected split character and it will halt 
+ *  immediately if the length of str falls below this point.  However, if the 
+ *  cb function destroys str, then it *must* return with a negative value, 
+ *  otherwise bsplitcb will continue in an undefined manner.
+ */
+int bsplitcb (const_bstring str, unsigned char splitChar, int pos,
+       int (* cb) (void * parm, int ofs, int len), void * parm) {
+int i, p, ret;
+
+       if (cb == NULL || str == NULL || pos < 0 || pos > str->slen) 
+               return BSTR_ERR;
+
+       p = pos;
+       do {
+               for (i=p; i < str->slen; i++) {
+                       if (str->data[i] == splitChar) break;
+               }
+               if ((ret = cb (parm, p, i - p)) < 0) return ret;
+               p = i + 1;
+       } while (p <= str->slen);
+       return BSTR_OK;
+}
+
+/*  int bsplitscb (const_bstring str, const_bstring splitStr, int pos,
+ *     int (* cb) (void * parm, int ofs, int len), void * parm)
+ *
+ *  Iterate the set of disjoint sequential substrings over str divided by any 
+ *  of the characters in splitStr.  An empty splitStr causes the whole str to
+ *  be iterated once.
+ *
+ *  Note: Non-destructive modification of str from within the cb function 
+ *  while performing this split is not undefined.  bsplitscb behaves in 
+ *  sequential lock step with calls to cb.  I.e., after returning from a cb 
+ *  that return a non-negative integer, bsplitscb continues from the position 
+ *  1 character after the last detected split character and it will halt 
+ *  immediately if the length of str falls below this point.  However, if the 
+ *  cb function destroys str, then it *must* return with a negative value, 
+ *  otherwise bsplitscb will continue in an undefined manner.
+ */
+int bsplitscb (const_bstring str, const_bstring splitStr, int pos,
+       int (* cb) (void * parm, int ofs, int len), void * parm) {
+struct charField chrs;
+int i, p, ret;
+
+       if (cb == NULL || str == NULL || pos < 0 || pos > str->slen 
+        || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR;
+       if (splitStr->slen == 0) {
+               if ((ret = cb (parm, 0, str->slen)) > 0) ret = 0;
+               return ret;
+       }
+
+       if (splitStr->slen == 1) 
+               return bsplitcb (str, splitStr->data[0], pos, cb, parm);
+
+       buildCharField (&chrs, splitStr);
+
+       p = pos;
+       do {
+               for (i=p; i < str->slen; i++) {
+                       if (testInCharField (&chrs, str->data[i])) break;
+               }
+               if ((ret = cb (parm, p, i - p)) < 0) return ret;
+               p = i + 1;
+       } while (p <= str->slen);
+       return BSTR_OK;
+}
+
+/*  int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos,
+ *     int (* cb) (void * parm, int ofs, int len), void * parm)
+ *
+ *  Iterate the set of disjoint sequential substrings over str divided by the 
+ *  substring splitStr.  An empty splitStr causes the whole str to be 
+ *  iterated once.
+ *
+ *  Note: Non-destructive modification of str from within the cb function 
+ *  while performing this split is not undefined.  bsplitstrcb behaves in 
+ *  sequential lock step with calls to cb.  I.e., after returning from a cb 
+ *  that return a non-negative integer, bsplitscb continues from the position 
+ *  1 character after the last detected split character and it will halt 
+ *  immediately if the length of str falls below this point.  However, if the 
+ *  cb function destroys str, then it *must* return with a negative value, 
+ *  otherwise bsplitscb will continue in an undefined manner.
+ */
+int bsplitstrcb (const_bstring str, const_bstring splitStr, int pos,
+       int (* cb) (void * parm, int ofs, int len), void * parm) {
+int i, p, ret;
+
+       if (cb == NULL || str == NULL || pos < 0 || pos > str->slen 
+        || splitStr == NULL || splitStr->slen < 0) return BSTR_ERR;
+
+       if (0 == splitStr->slen) {
+               for (i=pos; i < str->slen; i++) {
+                       if ((ret = cb (parm, i, 1)) < 0) return ret;
+               }
+               return BSTR_OK;
+       }
+
+       if (splitStr->slen == 1) 
+               return bsplitcb (str, splitStr->data[0], pos, cb, parm);
+
+       for (i=p=pos; i <= str->slen - splitStr->slen; i++) {
+               if (0 == bstr__memcmp (splitStr->data, str->data + i, splitStr->slen)) {
+                       if ((ret = cb (parm, p, i - p)) < 0) return ret;
+                       i += splitStr->slen;
+                       p = i;
+               }
+       }
+       if ((ret = cb (parm, p, str->slen - p)) < 0) return ret;
+       return BSTR_OK;
+}
+
+struct genBstrList {
+       bstring b;
+       struct bstrList * bl;
+};
+
+static int bscb (void * parm, int ofs, int len) {
+struct genBstrList * g = (struct genBstrList *) parm;
+       if (g->bl->qty >= g->bl->mlen) {
+               int mlen = g->bl->mlen * 2;
+               bstring * tbl;
+
+               while (g->bl->qty >= mlen) {
+                       if (mlen < g->bl->mlen) return BSTR_ERR;
+                       mlen += mlen;
+               }
+
+               tbl = (bstring *) bstr__realloc (g->bl->entry, sizeof (bstring) * mlen);
+               if (tbl == NULL) return BSTR_ERR;
+
+               g->bl->entry = tbl;
+               g->bl->mlen = mlen;
+       }
+
+       g->bl->entry[g->bl->qty] = bmidstr (g->b, ofs, len);
+       g->bl->qty++;
+       return BSTR_OK;
+}
+
+/*  struct bstrList * bsplit (const_bstring str, unsigned char splitChar)
+ *
+ *  Create an array of sequential substrings from str divided by the character
+ *  splitChar.  
+ */
+struct bstrList * bsplit (const_bstring str, unsigned char splitChar) {
+struct genBstrList g;
+
+       if (str == NULL || str->data == NULL || str->slen < 0) return NULL;
+
+       g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList));
+       if (g.bl == NULL) return NULL;
+       g.bl->mlen = 4;
+       g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring));
+       if (NULL == g.bl->entry) {
+               bstr__free (g.bl);
+               return NULL;
+       }
+
+       g.b = (bstring) str;
+       g.bl->qty = 0;
+       if (bsplitcb (str, splitChar, 0, bscb, &g) < 0) {
+               bstrListDestroy (g.bl);
+               return NULL;
+       }
+       return g.bl;
+}
+
+/*  struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr)
+ *
+ *  Create an array of sequential substrings from str divided by the entire
+ *  substring splitStr.
+ */
+struct bstrList * bsplitstr (const_bstring str, const_bstring splitStr) {
+struct genBstrList g;
+
+       if (str == NULL || str->data == NULL || str->slen < 0) return NULL;
+
+       g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList));
+       if (g.bl == NULL) return NULL;
+       g.bl->mlen = 4;
+       g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring));
+       if (NULL == g.bl->entry) {
+               bstr__free (g.bl);
+               return NULL;
+       }
+
+       g.b = (bstring) str;
+       g.bl->qty = 0;
+       if (bsplitstrcb (str, splitStr, 0, bscb, &g) < 0) {
+               bstrListDestroy (g.bl);
+               return NULL;
+       }
+       return g.bl;
+}
+
+/*  struct bstrList * bsplits (const_bstring str, bstring splitStr)
+ *
+ *  Create an array of sequential substrings from str divided by any of the 
+ *  characters in splitStr.  An empty splitStr causes a single entry bstrList
+ *  containing a copy of str to be returned.
+ */
+struct bstrList * bsplits (const_bstring str, const_bstring splitStr) {
+struct genBstrList g;
+
+       if (     str == NULL ||      str->slen < 0 ||      str->data == NULL ||
+           splitStr == NULL || splitStr->slen < 0 || splitStr->data == NULL)
+               return NULL;
+
+       g.bl = (struct bstrList *) bstr__alloc (sizeof (struct bstrList));
+       if (g.bl == NULL) return NULL;
+       g.bl->mlen = 4;
+       g.bl->entry = (bstring *) bstr__alloc (g.bl->mlen * sizeof (bstring));
+       if (NULL == g.bl->entry) {
+               bstr__free (g.bl);
+               return NULL;
+       }
+       g.b = (bstring) str;
+       g.bl->qty = 0;
+
+       if (bsplitscb (str, splitStr, 0, bscb, &g) < 0) {
+               bstrListDestroy (g.bl);
+               return NULL;
+       }
+       return g.bl;
+}
+
+#if defined (__TURBOC__) && !defined (__BORLANDC__)
+# ifndef BSTRLIB_NOVSNP
+#  define BSTRLIB_NOVSNP
+# endif
+#endif
+
+/* Give WATCOM C/C++, MSVC some latitude for their non-support of vsnprintf */
+#if defined(__WATCOMC__) || defined(_MSC_VER)
+#define exvsnprintf(r,b,n,f,a) {r = _vsnprintf (b,n,f,a);}
+#else
+#ifdef BSTRLIB_NOVSNP
+/* This is just a hack.  If you are using a system without a vsnprintf, it is 
+   not recommended that bformat be used at all. */
+#define exvsnprintf(r,b,n,f,a) {vsprintf (b,f,a); r = -1;}
+#define START_VSNBUFF (256)
+#else
+
+#ifdef __GNUC__
+/* Something is making gcc complain about this prototype not being here, so 
+   I've just gone ahead and put it in. */
+extern int vsnprintf (char *buf, size_t count, const char *format, va_list arg);
+#endif
+
+#define exvsnprintf(r,b,n,f,a) {r = vsnprintf (b,n,f,a);}
+#endif
+#endif
+
+#if !defined (BSTRLIB_NOVSNP)
+
+#ifndef START_VSNBUFF
+#define START_VSNBUFF (16)
+#endif
+
+/* On IRIX vsnprintf returns n-1 when the operation would overflow the target 
+   buffer, WATCOM and MSVC both return -1, while C99 requires that the 
+   returned value be exactly what the length would be if the buffer would be
+   large enough.  This leads to the idea that if the return value is larger 
+   than n, then changing n to the return value will reduce the number of
+   iterations required. */
+
+/*  int bformata (bstring b, const char * fmt, ...)
+ *
+ *  After the first parameter, it takes the same parameters as printf (), but 
+ *  rather than outputting results to stdio, it appends the results to 
+ *  a bstring which contains what would have been output. Note that if there 
+ *  is an early generation of a '\0' character, the bstring will be truncated 
+ *  to this end point.
+ */
+int bformata (bstring b, const char * fmt, ...) {
+va_list arglist;
+bstring buff;
+int n, r;
+
+       if (b == NULL || fmt == NULL || b->data == NULL || b->mlen <= 0 
+        || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR;
+
+       /* Since the length is not determinable beforehand, a search is
+          performed using the truncating "vsnprintf" call (to avoid buffer
+          overflows) on increasing potential sizes for the output result. */
+
+       if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF;
+       if (NULL == (buff = bfromcstralloc (n + 2, ""))) {
+               n = 1;
+               if (NULL == (buff = bfromcstralloc (n + 2, ""))) return BSTR_ERR;
+       }
+
+       for (;;) {
+               va_start (arglist, fmt);
+               exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist);
+               va_end (arglist);
+
+               buff->data[n] = (unsigned char) '\0';
+               buff->slen = (int) (strlen) ((char *) buff->data);
+
+               if (buff->slen < n) break;
+
+               if (r > n) n = r; else n += n;
+
+               if (BSTR_OK != balloc (buff, n + 2)) {
+                       bdestroy (buff);
+                       return BSTR_ERR;
+               }
+       }
+
+       r = bconcat (b, buff);
+       bdestroy (buff);
+       return r;
+}
+
+/*  int bassignformat (bstring b, const char * fmt, ...)
+ *
+ *  After the first parameter, it takes the same parameters as printf (), but 
+ *  rather than outputting results to stdio, it outputs the results to 
+ *  the bstring parameter b. Note that if there is an early generation of a 
+ *  '\0' character, the bstring will be truncated to this end point.
+ */
+int bassignformat (bstring b, const char * fmt, ...) {
+va_list arglist;
+bstring buff;
+int n, r;
+
+       if (b == NULL || fmt == NULL || b->data == NULL || b->mlen <= 0 
+        || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR;
+
+       /* Since the length is not determinable beforehand, a search is
+          performed using the truncating "vsnprintf" call (to avoid buffer
+          overflows) on increasing potential sizes for the output result. */
+
+       if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF;
+       if (NULL == (buff = bfromcstralloc (n + 2, ""))) {
+               n = 1;
+               if (NULL == (buff = bfromcstralloc (n + 2, ""))) return BSTR_ERR;
+       }
+
+       for (;;) {
+               va_start (arglist, fmt);
+               exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist);
+               va_end (arglist);
+
+               buff->data[n] = (unsigned char) '\0';
+               buff->slen = (int) (strlen) ((char *) buff->data);
+
+               if (buff->slen < n) break;
+
+               if (r > n) n = r; else n += n;
+
+               if (BSTR_OK != balloc (buff, n + 2)) {
+                       bdestroy (buff);
+                       return BSTR_ERR;
+               }
+       }
+
+       r = bassign (b, buff);
+       bdestroy (buff);
+       return r;
+}
+
+/*  bstring bformat (const char * fmt, ...)
+ *
+ *  Takes the same parameters as printf (), but rather than outputting results
+ *  to stdio, it forms a bstring which contains what would have been output.
+ *  Note that if there is an early generation of a '\0' character, the 
+ *  bstring will be truncated to this end point.
+ */
+bstring bformat (const char * fmt, ...) {
+va_list arglist;
+bstring buff;
+int n, r;
+
+       if (fmt == NULL) return NULL;
+
+       /* Since the length is not determinable beforehand, a search is
+          performed using the truncating "vsnprintf" call (to avoid buffer
+          overflows) on increasing potential sizes for the output result. */
+
+       if ((n = (int) (2*strlen (fmt))) < START_VSNBUFF) n = START_VSNBUFF;
+       if (NULL == (buff = bfromcstralloc (n + 2, ""))) {
+               n = 1;
+               if (NULL == (buff = bfromcstralloc (n + 2, ""))) return NULL;
+       }
+
+       for (;;) {
+               va_start (arglist, fmt);
+               exvsnprintf (r, (char *) buff->data, n + 1, fmt, arglist);
+               va_end (arglist);
+
+               buff->data[n] = (unsigned char) '\0';
+               buff->slen = (int) (strlen) ((char *) buff->data);
+
+               if (buff->slen < n) break;
+
+               if (r > n) n = r; else n += n;
+
+               if (BSTR_OK != balloc (buff, n + 2)) {
+                       bdestroy (buff);
+                       return NULL;
+               }
+       }
+
+       return buff;
+}
+
+/*  int bvcformata (bstring b, int count, const char * fmt, va_list arglist)
+ *
+ *  The bvcformata function formats data under control of the format control 
+ *  string fmt and attempts to append the result to b.  The fmt parameter is 
+ *  the same as that of the printf function.  The variable argument list is 
+ *  replaced with arglist, which has been initialized by the va_start macro.
+ *  The size of the output is upper bounded by count.  If the required output
+ *  exceeds count, the string b is not augmented with any contents and a value
+ *  below BSTR_ERR is returned.  If a value below -count is returned then it
+ *  is recommended that the negative of this value be used as an update to the
+ *  count in a subsequent pass.  On other errors, such as running out of 
+ *  memory, parameter errors or numeric wrap around BSTR_ERR is returned.  
+ *  BSTR_OK is returned when the output is successfully generated and 
+ *  appended to b.
+ *
+ *  Note: There is no sanity checking of arglist, and this function is
+ *  destructive of the contents of b from the b->slen point onward.  If there 
+ *  is an early generation of a '\0' character, the bstring will be truncated 
+ *  to this end point.
+ */
+int bvcformata (bstring b, int count, const char * fmt, va_list arg) {
+int n, r, l;
+
+       if (b == NULL || fmt == NULL || count <= 0 || b->data == NULL
+        || b->mlen <= 0 || b->slen < 0 || b->slen > b->mlen) return BSTR_ERR;
+
+       if (count > (n = b->slen + count) + 2) return BSTR_ERR;
+       if (BSTR_OK != balloc (b, n + 2)) return BSTR_ERR;
+
+       exvsnprintf (r, (char *) b->data + b->slen, count + 2, fmt, arg);
+
+       /* Did the operation complete successfully within bounds? */
+
+       if (n >= (l = b->slen + (int) (strlen) ((const char *) b->data + b->slen))) {
+               b->slen = l;
+               return BSTR_OK;
+       }
+
+       /* Abort, since the buffer was not large enough.  The return value 
+          tries to help set what the retry length should be. */
+
+       b->data[b->slen] = '\0';
+       if (r > count+1) l = r; else {
+               l = count+count;
+               if (count > l) l = INT_MAX;
+       }
+       n = -l;
+       if (n > BSTR_ERR-1) n = BSTR_ERR-1;
+       return n;
+}
+
+#endif
index 75b41c112984dcd5bf827acc9189c724d1dc5f7e..cd3ca24fda0587571a63c6498934b8f12f1ce153 100644 (file)
@@ -11,7 +11,7 @@ libcnid_cdb_la_SOURCES = cnid_cdb_add.c \
                         cnid_cdb_rebuild_add.c \
                         cnid_cdb.h
 libcnid_cdb_la_CFLAGS = @BDB_CFLAGS@
-libcnid_cdb_la_LIBADD = @BDB_LIBS@
+libcnid_cdb_la_LIBADD = @BDB_LIBS@ @PTHREAD_LIBS@
 
 if USE_CDB_BACKEND
 noinst_LTLIBRARIES = libcnid_cdb.la
index bf501fd5e9e52aa5bcc9fd7c85314771b5379307..10ecc78d8c34efaf51365820954b1ff5d06c0f6a 100644 (file)
@@ -1,7 +1,4 @@
 /*
-
- * $Id: cnid_cdb_open.c,v 1.5 2010-03-31 09:47:32 franklahm Exp $
- *
  * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
  * All Rights Reserved. See COPYRIGHT.
  *
@@ -40,6 +37,8 @@
 #endif /* HAVE_CONFIG_H */
 
 #ifdef CNID_BACKEND_CDB
+
+#include <atalk/cnid_private.h>
 #include "cnid_cdb_private.h"
 
 #ifndef MIN
 #define DBHOMELEN    8
 #define DBLEN        10
 
-/* we version the did/name database so that we can change the format
- * if necessary. the key is in the form of a did/name pair. in this case,
- * we use 0/0. */
-#define DBVERSION_KEY    "\0\0\0\0\0"
-#define DBVERSION_KEYLEN 5
-#define DBVERSION1       0x00000001U
-#define DBVERSION        DBVERSION1
-
 #define DBOPTIONS    (DB_CREATE | DB_INIT_CDB | DB_INIT_MPOOL)
 
 #define MAXITER     0xFFFF      /* maximum number of simultaneously open CNID
@@ -355,42 +346,27 @@ struct _cnid_db *cnid_cdb_open(struct cnid_open_args *args)
         goto fail_appinit;
     }
  
-#if 0
-    DBT key, pkey, data;
     /* ---------------------- */
-    /* Check for version.  This way we can update the database if we need
-     * to change the format in any way. */
-    memset(&key, 0, sizeof(key));
-    memset(&pkey, 0, sizeof(DBT));
-    memset(&data, 0, sizeof(data));
-    key.data = DBVERSION_KEY;
-    key.size = DBVERSION_KEYLEN;
+    /* Check for version. "cdb" only supports CNID_VERSION_0, cf cnid_private.h */
 
-    if ((rc = db->db_didname->pget(db->db_didname, NULL, &key, &pkey, &data, 0)) != 0) {
-        int ret;
-        {
-            u_int32_t version = htonl(DBVERSION);
+    DBT key, data;
+    uint32_t version;
 
-            data.data = &version;
-            data.size = sizeof(version);
-        }
-        if ((ret = db->db_didname->put(db->db_cnid, NULL, &key, &data,
-                                       DB_NOOVERWRITE))) {
-            LOG(log_error, logtype_default, "cnid_open: Error putting new version: %s",
-                db_strerror(ret));
-            db->db_didname->close(db->db_didname, 0);
+    memset(&key, 0, sizeof(key));
+    memset(&data, 0, sizeof(data));
+    key.data = ROOTINFO_KEY;
+    key.size = ROOTINFO_KEYLEN;
+
+    if ((rc = db->db_cnid->get(db->db_cnid, NULL, &key, &data, 0)) == 0) {
+        /* If not found, ignore it */
+        memcpy(&version, data.data + CNID_DID_OFS, sizeof(version));
+        version = ntohl(version);
+        LOG(log_debug, logtype_default, "CNID db version %u", version);
+        if (version != CNID_VERSION_0) {
+            LOG(log_error, logtype_default, "Unsupported CNID db version %u, use CNID backend \"dbd\"", version);
             goto fail_appinit;
         }
     }
-#endif
-
-    /* TODO In the future we might check for version number here. */
-#if 0
-    memcpy(&version, data.data, sizeof(version));
-    if (version != ntohl(DBVERSION)) {
-        /* Do stuff here. */
-    }
-#endif /* 0 */
 
     db_env_set_func_yield(my_yield);
     return cdb;
index 9231cf5508f72648af2dd114da9143879cb0b978..68136f9f196594325119d762eae8ec1154f06397 100644 (file)
@@ -1,6 +1,4 @@
 /* 
- * $Id: cnid.c,v 1.13 2010-03-31 09:47:32 franklahm Exp $
- *
  * Copyright (c) 2003 the Netatalk Team
  * Copyright (c) 2003 Rafal Lewczuk <rlewczuk@pronet.pl>
  * 
@@ -166,7 +164,7 @@ struct _cnid_db *cnid_open(const char *volpath, mode_t mask, char *type, int fla
 static void block_signal( u_int32_t flags)
 {
     if ((flags & CNID_FLAG_BLOCK)) {
-        sigprocmask(SIG_BLOCK, &sigblockset, NULL);
+        pthread_sigmask(SIG_BLOCK, &sigblockset, NULL);
     }
 }
 
@@ -174,7 +172,7 @@ static void block_signal( u_int32_t flags)
 static void unblock_signal(u_int32_t flags)
 {
     if ((flags & CNID_FLAG_BLOCK)) {
-        sigprocmask(SIG_UNBLOCK, &sigblockset, NULL);
+        pthread_sigmask(SIG_UNBLOCK, &sigblockset, NULL);
     }
 }
 
@@ -216,7 +214,7 @@ u_int32_t flags;
 
 /* --------------- */
 cnid_t cnid_add(struct _cnid_db *cdb, const struct stat *st, const cnid_t did, 
-                       char *name, const size_t len, cnid_t hint)
+                const char *name, const size_t len, cnid_t hint)
 {
 cnid_t ret;
 
@@ -272,9 +270,9 @@ time_t t;
 
 /* --------------- */
 cnid_t cnid_lookup(struct _cnid_db *cdb, const struct stat *st, const cnid_t did,
-                       char *name, const size_t len)
+                   char *name, const size_t len)
 {
-cnid_t ret;
+    cnid_t ret;
 
     block_signal(cdb->flags);
     ret = valide(cdb->cnid_lookup(cdb, st, did, name, len));
@@ -282,6 +280,22 @@ cnid_t ret;
     return ret;
 }
 
+/* --------------- */
+int cnid_find(struct _cnid_db *cdb, const char *name, size_t namelen, void *buffer, size_t buflen)
+{
+    int ret;
+    
+    if (cdb->cnid_find == NULL) {
+        LOG(log_error, logtype_cnid, "cnid_find not supported by CNID backend");        
+        return -1;
+    }
+
+    block_signal(cdb->flags);
+    ret = cdb->cnid_find(cdb, name, namelen, buffer, buflen);
+    unblock_signal(cdb->flags);
+    return ret;
+}
+
 /* --------------- */
 char *cnid_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len)
 {
index dfeb97cecfa6d3f4c0a3a4b2684bdcde1b8089ad..990248e45e439a22bc4de87377ae8fbe7e1e95f7 100644 (file)
@@ -1,7 +1,6 @@
 /*
- * $Id: cnid_dbd.c,v 1.17 2010-03-31 09:47:32 franklahm Exp $
- *
  * Copyright (C) Joerg Lenneis 2003
+ * Copyright (C) Frank Lahm 2010
  * All Rights Reserved.  See COPYING.
  */
 
 #include <atalk/logger.h>
 #include <atalk/adouble.h>
 #include <atalk/cnid.h>
-#include "cnid_dbd.h"
 #include <atalk/cnid_dbd_private.h>
+#include <atalk/util.h>
+
+#include "cnid_dbd.h"
 
 #ifndef SOL_TCP
 #define SOL_TCP IPPROTO_TCP
 #endif /* ! SOL_TCP */
 
+/* Wait MAX_DELAY seconds before a request to the CNID server times out */
+#define MAX_DELAY 20
+#define ONE_DELAY 5
+
 static void RQST_RESET(struct cnid_dbd_rqst  *r)
 {
     memset(r, 0, sizeof(struct cnid_dbd_rqst ));
 }
 
-/* ----------- */
-#define MAX_DELAY 10
-
-/* *MUST* be < afp tickle or it's never triggered (got EINTR first) */
-#define SOCK_DELAY 11
-
 static void delay(int sec)
 {
     struct timeval tv;
@@ -69,10 +68,11 @@ static void delay(int sec)
 static int tsock_getfd(const char *host, const char *port)
 {
     int sock = -1;
-    struct timeval tv;
     int attr;
     int err;
     struct addrinfo hints, *servinfo, *p;
+    int optval;
+    socklen_t optlen = sizeof(optval);
 
     /* Prepare hint for getaddrinfo */
     memset(&hints, 0, sizeof hints);
@@ -81,48 +81,93 @@ static int tsock_getfd(const char *host, const char *port)
     hints.ai_flags = AI_NUMERICSERV;
 
     if ((err = getaddrinfo(host, port, &hints, &servinfo)) != 0) {
-        LOG(log_error, logtype_default, "tsock_getfd: getaddrinfo: CNID server %s:%s : %s\n", host, port, gai_strerror(err));
+        LOG(log_error, logtype_default, "tsock_getfd: getaddrinfo: CNID server %s:%s : %s\n",
+            host, port, gai_strerror(err));
         return -1;
     }
 
     /* loop through all the results and bind to the first we can */
     for (p = servinfo; p != NULL; p = p->ai_next) {
         if ((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
-            LOG(log_info, logtype_default, "tsock_getfd: socket CNID server %s:: %s", host, strerror(errno));
-                continue;
+            LOG(log_info, logtype_default, "tsock_getfd: socket CNID server %s:: %s",
+                host, strerror(errno));
+            continue;
         }
 
         attr = 1;
         if (setsockopt(sock, SOL_TCP, TCP_NODELAY, &attr, sizeof(attr)) == -1) {
-            LOG(log_error, logtype_cnid, "getfd: set TCP_NODELAY CNID server %s: %s", host, strerror(errno));
+            LOG(log_error, logtype_cnid, "getfd: set TCP_NODELAY CNID server %s: %s",
+                host, strerror(errno));
             close(sock);
-            continue;
-        }
-        
-        tv.tv_sec = SOCK_DELAY;
-        tv.tv_usec = 0;
-        if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
-            LOG(log_error, logtype_cnid, "getfd: set SO_RCVTIMEO CNID server %s: %s", host, strerror(errno));
-            close(sock);
-            continue;
+            sock = -1;
+            return -1;
         }
 
-        tv.tv_sec = SOCK_DELAY;
-        tv.tv_usec = 0;
-        if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
-            LOG(log_error, logtype_cnid, "getfd: set SO_SNDTIMEO CNID server %s: %s", host, strerror(errno));
+        if (setnonblock(sock, 1) != 0) {
+            LOG(log_error, logtype_cnid, "getfd: setnonblock: %s", strerror(err));
             close(sock);
-            continue;
+            sock = -1;
+            return -1;
         }
-        
+
         if (connect(sock, p->ai_addr, p->ai_addrlen) == -1) {
-            err = errno;
-            close(sock);
-            sock=-1;
-            LOG(log_error, logtype_cnid, "getfd: connect CNID server %s: %s", host, strerror(err));
-            continue;
+            if (errno == EINPROGRESS) {
+                struct timeval tv;
+                tv.tv_usec = 0;
+                tv.tv_sec  = 5; /* give it five seconds ... */
+                fd_set wfds;
+                FD_ZERO(&wfds);
+                FD_SET(sock, &wfds);
+
+                if ((err = select(sock + 1, NULL, &wfds, NULL, &tv)) == 0) {
+                    /* timeout */
+                    LOG(log_error, logtype_cnid, "getfd: select timed out for CNID server %s",
+                        host);
+                    close(sock);
+                    sock = -1;
+                    continue;
+                }
+                if (err == -1) {
+                    /* select failed */
+                    LOG(log_error, logtype_cnid, "getfd: select failed for CNID server %s",
+                        host);
+                    close(sock);
+                    sock = -1;
+                    continue;
+                }
+
+                if ( ! FD_ISSET(sock, &wfds)) {
+                    /* give up */
+                    LOG(log_error, logtype_cnid, "getfd: socket not ready connecting to %s",
+                        host);
+                    close(sock);
+                    sock = -1;
+                    continue;
+                }
+
+                if ((err = getsockopt(sock, SOL_SOCKET, SO_ERROR, &optval, &optlen)) != 0 || optval != 0) {
+                    if (err != 0) {
+                        /* somethings very wrong */
+                        LOG(log_error, logtype_cnid, "getfd: getsockopt error with CNID server %s: %s",
+                            host, strerror(errno));
+                    } else {
+                        errno = optval;
+                        LOG(log_error, logtype_cnid, "getfd: getsockopt says: %s",
+                            strerror(errno));
+                    }
+                    close(sock);
+                    sock = -1;
+                    continue;
+                }
+            } else {
+                LOG(log_error, logtype_cnid, "getfd: connect CNID server %s: %s",
+                    host, strerror(errno));
+                close(sock);
+                sock = -1;
+                continue;
+            }
         }
-        
+
         /* We've got a socket */
         break;
     }
@@ -130,49 +175,57 @@ static int tsock_getfd(const char *host, const char *port)
     freeaddrinfo(servinfo);
 
     if (p == NULL) {
-        LOG(log_error, logtype_cnid, "tsock_getfd: no suitable network config from CNID server %s:%s", host, port);
+        errno = optval;
+        LOG(log_error, logtype_cnid, "tsock_getfd: no suitable network config from CNID server (%s:%s): %s",
+            host, port, strerror(errno));
         return -1;
     }
 
     return(sock);
 }
 
-/* --------------------- */
-static int write_vec(int fd, struct iovec *iov, size_t towrite)
+/*!
+ * Write "towrite" bytes using writev on non-blocking fd
+ *
+ * Every short write is considered an error, transmit can handle that.
+ *
+ * @param fd      (r) socket fd which must be non-blocking
+ * @param iov     (r) iovec for writev
+ * @param towrite (r) number of bytes in all iovec elements
+ * @param vecs    (r) number of iovecs in array
+ *
+ * @returns "towrite" bytes written or -1 on error
+ */
+static int write_vec(int fd, struct iovec *iov, ssize_t towrite, int vecs)
 {
     ssize_t len;
-    size_t len1;
+    int slept = 0;
+    int sleepsecs;
 
-    LOG(log_maxdebug, logtype_cnid, "write_vec: request to write %d bytes", towrite);
+    while (1) {
+        if (((len = writev(fd, iov, vecs)) == -1 && errno == EINTR))
+            continue;
 
-    len1 =  iov[1].iov_len;
-    while (towrite > 0) {
-        if (((len = writev(fd, iov, 2)) == -1 && errno == EINTR) || !len)
+        if ((! slept) && len == -1 && errno == EAGAIN) {
+            sleepsecs = 2;
+            while ((sleepsecs = sleep(sleepsecs)));
+            slept = 1;
             continue;
+        }
 
-        if ((size_t)len == towrite) /* wrote everything out */
+        if (len == towrite) /* wrote everything out */
             break;
-        else if (len < 0) { /* error */
-            return -1;
-        }
 
-        towrite -= len;
-        if (towrite > len1) { /* skip part of header */
-            iov[0].iov_base = (char *) iov[0].iov_base + len;
-            iov[0].iov_len -= len;
-        } else { /* skip to data */
-            if (iov[0].iov_len) {
-                len -= iov[0].iov_len;
-                iov[0].iov_len = 0;
-            }
-            iov[1].iov_base = (char *) iov[1].iov_base + len;
-            iov[1].iov_len -= len;
-        }
+        if (len == -1)
+            LOG(log_error, logtype_cnid, "write_vec: %s", strerror(errno));
+        else
+            LOG(log_error, logtype_cnid, "write_vec: short write: %d", len);
+        return len;
     }
 
-    LOG(log_maxdebug, logtype_cnid, "write_vec: wrote %d bytes", towrite);
+    LOG(log_maxdebug, logtype_cnid, "write_vec: wrote %d bytes", len);
 
-    return 0;
+    return len;
 }
 
 /* --------------------- */
@@ -182,7 +235,7 @@ static int init_tsock(CNID_private *db)
     int len;
     struct iovec iov[2];
 
-    LOG(log_debug, logtype_cnid, "init_tsock: BEGIN. Opening volume '%s', CNID Server: %s/%s", 
+    LOG(log_debug, logtype_cnid, "init_tsock: BEGIN. Opening volume '%s', CNID Server: %s/%s",
         db->db_dir, db->cnidserver, db->cnidport);
 
     if ((fd = tsock_getfd(db->cnidserver, db->cnidport)) < 0)
@@ -196,7 +249,7 @@ static int init_tsock(CNID_private *db)
     iov[1].iov_base = db->db_dir;
     iov[1].iov_len  = len;
 
-    if (write_vec(fd, iov, len + sizeof(int)) < 0) {
+    if (write_vec(fd, iov, len + sizeof(int), 2) != len + sizeof(int)) {
         LOG(log_error, logtype_cnid, "init_tsock: Error/short write: %s", strerror(errno));
         close(fd);
         return -1;
@@ -212,34 +265,27 @@ static int send_packet(CNID_private *db, struct cnid_dbd_rqst *rqst)
 {
     struct iovec iov[2];
     size_t towrite;
-
-    LOG(log_maxdebug, logtype_cnid, "send_packet: BEGIN");
-
-    if (!rqst->namelen) {
-        if (write(db->fd, rqst, sizeof(struct cnid_dbd_rqst)) != sizeof(struct cnid_dbd_rqst)) {
-            LOG(log_warning, logtype_cnid, "send_packet: Error/short write rqst (db_dir %s): %s",
-                db->db_dir, strerror(errno));
-            return -1;
-        }
-        LOG(log_maxdebug, logtype_cnid, "send_packet: OK");
-        return 0;
-    }
+    int vecs;
 
     iov[0].iov_base = rqst;
     iov[0].iov_len  = sizeof(struct cnid_dbd_rqst);
+    towrite = sizeof(struct cnid_dbd_rqst);
+    vecs = 1;
 
-    iov[1].iov_base = rqst->name;
-    iov[1].iov_len  = rqst->namelen;
-
-    towrite = sizeof(struct cnid_dbd_rqst) +rqst->namelen;
+    if (rqst->namelen) {
+        iov[1].iov_base = rqst->name;
+        iov[1].iov_len  = rqst->namelen;
+        towrite += rqst->namelen;
+        vecs++;
+    }
 
-    if (write_vec(db->fd, iov, towrite) < 0) {
+    if (write_vec(db->fd, iov, towrite, vecs) != towrite) {
         LOG(log_warning, logtype_cnid, "send_packet: Error writev rqst (db_dir %s): %s",
             db->db_dir, strerror(errno));
         return -1;
     }
-    
-    LOG(log_maxdebug, logtype_cnid, "send_packet: OK");
+
+    LOG(log_maxdebug, logtype_cnid, "send_packet: {done}");
     return 0;
 }
 
@@ -266,28 +312,6 @@ static int dbd_reply_stamp(struct cnid_dbd_rply *rply)
     return 0;
 }
 
-/* ------------------- */
-static ssize_t dbd_read(int socket, void *data, const size_t length)
-{
-    size_t stored;
-    ssize_t len;
-  
-    stored = 0;
-    while (stored < length) {
-        len = read(socket, (u_int8_t *) data + stored, length - stored);
-        if (len == -1) {
-            if (errno == EINTR)
-                continue;
-            return -1;
-        }
-        else if (len > 0)
-            stored += len;
-        else
-            break;
-    }
-    return stored;
-}
-
 /* ---------------------
  * send a request and get reply
  * assume send is non blocking
@@ -299,19 +323,17 @@ static int dbd_rpc(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_dbd
     char *nametmp;
     size_t len;
 
-    LOG(log_maxdebug, logtype_cnid, "dbd_rpc: BEGIN");
-
     if (send_packet(db, rqst) < 0) {
         return -1;
     }
     len = rply->namelen;
     nametmp = rply->name;
 
-    ret = dbd_read(db->fd, rply, sizeof(struct cnid_dbd_rply));
+    ret = readt(db->fd, rply, sizeof(struct cnid_dbd_rply), 0, ONE_DELAY);
 
     if (ret != sizeof(struct cnid_dbd_rply)) {
         LOG(log_error, logtype_cnid, "dbd_rpc: Error reading header from fd (db_dir %s): %s",
-            db->db_dir, ret == -1?strerror(errno):"closed");
+            db->db_dir, ret == -1 ? strerror(errno) : "closed");
         rply->name = nametmp;
         return -1;
     }
@@ -322,13 +344,13 @@ static int dbd_rpc(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_dbd
             db->db_dir, rply->name, rply->namelen, len);
         return -1;
     }
-    if (rply->namelen && (ret = dbd_read(db->fd, rply->name, rply->namelen)) != (ssize_t)rply->namelen) {
+    if (rply->namelen && (ret = readt(db->fd, rply->name, rply->namelen, 0, ONE_DELAY)) != (ssize_t)rply->namelen) {
         LOG(log_error, logtype_cnid, "dbd_rpc: Error reading name from fd (db_dir %s): %s",
             db->db_dir, ret == -1?strerror(errno):"closed");
         return -1;
     }
 
-    LOG(log_maxdebug, logtype_cnid, "dbd_rpc: END");
+    LOG(log_maxdebug, logtype_cnid, "dbd_rpc: {done}");
 
     return 0;
 }
@@ -339,8 +361,6 @@ static int transmit(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_db
     time_t orig, t;
     int clean = 1; /* no errors so far - to prevent sleep on first try */
 
-    LOG(log_debug7, logtype_cnid, "transmit: BEGIN");
-
     if (db->changed) {
         /* volume and db don't have the same timestamp
          */
@@ -352,7 +372,7 @@ static int transmit(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_db
             struct cnid_dbd_rply rply_stamp;
             char  stamp[ADEDLEN_PRIVSYN];
 
-            LOG(log_debug, logtype_cnid, "transmit: connecting to cnid_dbd ...");
+            LOG(log_maxdebug, logtype_cnid, "transmit: connecting to cnid_dbd ...");
             if ((db->fd = init_tsock(db)) < 0) {
                 goto transmit_fail;
             }
@@ -374,18 +394,17 @@ static int transmit(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_db
                     return -1;
                 }
                 LOG(log_debug7, logtype_cnid, "transmit: ... OK.");
-            }
-            else {
+            } else { /* db->notfirst == 0 */
                 db->notfirst = 1;
                 if (db->client_stamp)
                     memcpy(db->client_stamp, stamp, ADEDLEN_PRIVSYN);
                 memcpy(db->stamp, stamp, ADEDLEN_PRIVSYN);
             }
-            LOG(log_debug, logtype_cnid, "transmit: succesfully attached to cnid_dbd for volume '%s' with stamp '%08lx'.", 
+            LOG(log_debug, logtype_cnid, "transmit: attached to '%s', stamp: '%08lx'.",
                 db->db_dir, *(uint64_t *)stamp);
         }
         if (!dbd_rpc(db, rqst, rply)) {
-            LOG(log_debug7, logtype_cnid, "transmit: END OK");
+            LOG(log_maxdebug, logtype_cnid, "transmit: {done}");
             return 0;
         }
     transmit_fail:
@@ -394,6 +413,12 @@ static int transmit(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_db
             db->fd = -1; /* FD not valid... will need to reconnect */
         }
 
+        if (errno == ECONNREFUSED) { /* errno carefully injected in tsock_getfd */
+            /* give up */
+            LOG(log_error, logtype_cnid, "transmit: connection refused (db_dir %s)", db->db_dir);
+            return -1;
+        }
+
         if (!clean) { /* don't sleep if just got disconnected by cnid server */
             time(&t);
             if (t - orig > MAX_DELAY) {
@@ -401,7 +426,7 @@ static int transmit(CNID_private *db, struct cnid_dbd_rqst *rqst, struct cnid_db
                 return -1;
             }
             /* sleep a little before retry */
-            delay(2);
+            delay(1);
         } else {
             clean = 0; /* false... next time sleep */
             time(&orig);
@@ -429,6 +454,7 @@ static struct _cnid_db *cnid_dbd_new(const char *volpath)
     cdb->cnid_delete = cnid_dbd_delete;
     cdb->cnid_get = cnid_dbd_get;
     cdb->cnid_lookup = cnid_dbd_lookup;
+    cdb->cnid_find = cnid_dbd_find;
     cdb->cnid_nextid = NULL;
     cdb->cnid_resolve = cnid_dbd_resolve;
     cdb->cnid_getstamp = cnid_dbd_getstamp;
@@ -654,11 +680,9 @@ char *cnid_dbd_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t le
     rqst.op = CNID_DBD_OP_RESOLVE;
     rqst.cnid = *id;
 
-    /* This mimicks the behaviour of the "regular" cnid_resolve. So far,
-       nobody uses the content of buffer. It only provides space for the
-       name in the caller. */
-    rply.name = (char *)buffer + CNID_HEADER_LEN;
-    rply.namelen = len - CNID_HEADER_LEN;
+    /* Pass buffer to transmit so it can stuff the reply data there */
+    rply.name = (char *)buffer;
+    rply.namelen = len;
 
     if (transmit(db, &rqst, &rply) < 0) {
         errno = CNID_ERR_DB;
@@ -669,7 +693,7 @@ char *cnid_dbd_resolve(struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t le
     switch (rply.result) {
     case CNID_DBD_RES_OK:
         *id = rply.did;
-        name = rply.name;
+        name = rply.name + CNID_NAME_OFS;
         LOG(log_debug, logtype_cnid, "cnid_dbd_resolve: resolved did: %u, name: '%s'", ntohl(*id), name);
         break;
     case CNID_DBD_RES_NOTFOUND:
@@ -766,6 +790,61 @@ cnid_t cnid_dbd_lookup(struct _cnid_db *cdb, const struct stat *st, const cnid_t
     return id;
 }
 
+/* ---------------------- */
+int cnid_dbd_find(struct _cnid_db *cdb, char *name, size_t namelen, void *buffer, size_t buflen)
+{
+    CNID_private *db;
+    struct cnid_dbd_rqst rqst;
+    struct cnid_dbd_rply rply;
+    int count;
+
+    if (!cdb || !(db = cdb->_private) || !name) {
+        LOG(log_error, logtype_cnid, "cnid_find: Parameter error");
+        errno = CNID_ERR_PARAM;
+        return CNID_INVALID;
+    }
+
+    if (namelen > MAXPATHLEN) {
+        LOG(log_error, logtype_cnid, "cnid_find: Path name is too long");
+        errno = CNID_ERR_PATH;
+        return CNID_INVALID;
+    }
+
+    LOG(log_debug, logtype_cnid, "cnid_find(\"%s\")", name);
+
+    RQST_RESET(&rqst);
+    rqst.op = CNID_DBD_OP_SEARCH;
+
+    rqst.name = name;
+    rqst.namelen = namelen;
+
+    rply.name = buffer;
+    rply.namelen = buflen;
+
+    if (transmit(db, &rqst, &rply) < 0) {
+        errno = CNID_ERR_DB;
+        return CNID_INVALID;
+    }
+
+    switch (rply.result) {
+    case CNID_DBD_RES_OK:
+        count = rply.namelen / sizeof(cnid_t);
+        LOG(log_debug, logtype_cnid, "cnid_find: got %d matches", count);
+        break;
+    case CNID_DBD_RES_NOTFOUND:
+        count = 0;
+        break;
+    case CNID_DBD_RES_ERR_DB:
+        errno = CNID_ERR_DB;
+        count = -1;
+        break;
+    default:
+        abort();
+    }
+
+    return count;
+}
+
 /* ---------------------- */
 int cnid_dbd_update(struct _cnid_db *cdb, const cnid_t id, const struct stat *st,
                     const cnid_t did, char *name, const size_t len)
index 6d43f9a0a27ccd1bfc5bd7dad71a3572fc72e719..1f645908358b442633beae7e730e9789be690975 100644 (file)
@@ -1,7 +1,6 @@
 /*
- * $Id: cnid_dbd.h,v 1.6 2010-03-31 09:47:32 franklahm Exp $
- *
  * Copyright (C) Joerg Lenneis 2003
+ * Copyright (C) Frank Lahm 2010
  * All Rights Reserved.  See COPYING.
  */
 
 
 extern struct _cnid_module cnid_dbd_module;
 extern struct _cnid_db *cnid_dbd_open (struct cnid_open_args *args);
-extern void cnid_dbd_close (struct _cnid_db *);
-extern cnid_t cnid_dbd_add (struct _cnid_db *, const struct stat *, const cnid_t,
-                           char *, const size_t, cnid_t);
-extern cnid_t cnid_dbd_get (struct _cnid_db *, const cnid_t, char *, const size_t); 
-extern char *cnid_dbd_resolve (struct _cnid_db *, cnid_t *, void *, size_t ); 
-extern int cnid_dbd_getstamp (struct _cnid_db *, void *, const size_t ); 
-extern cnid_t cnid_dbd_lookup (struct _cnid_db *, const struct stat *, const cnid_t,
-                              char *, const size_t);
-extern int cnid_dbd_update (struct _cnid_db *, const cnid_t, const struct stat *,
-                           const cnid_t, char *, size_t);
-extern int cnid_dbd_delete (struct _cnid_db *, const cnid_t);
-extern cnid_t cnid_dbd_rebuild_add (struct _cnid_db *, const struct stat *,
-                const cnid_t, char *, const size_t, cnid_t);
+extern void   cnid_dbd_close      (struct _cnid_db *);
+extern cnid_t cnid_dbd_add        (struct _cnid_db *, const struct stat *, const cnid_t,
+                                   char *, const size_t, cnid_t);
+extern cnid_t cnid_dbd_get        (struct _cnid_db *, const cnid_t, char *, const size_t); 
+extern char  *cnid_dbd_resolve    (struct _cnid_db *, cnid_t *, void *, size_t ); 
+extern int    cnid_dbd_getstamp   (struct _cnid_db *, void *, const size_t ); 
+extern cnid_t cnid_dbd_lookup     (struct _cnid_db *, const struct stat *, const cnid_t,
+                                   char *, const size_t);
+extern int    cnid_dbd_find       (struct _cnid_db *cdb, char *name, size_t namelen,
+                                   void *buffer, size_t buflen);
+extern int    cnid_dbd_update     (struct _cnid_db *, const cnid_t, const struct stat *,
+                                   const cnid_t, char *, size_t);
+extern int    cnid_dbd_delete     (struct _cnid_db *, const cnid_t);
+extern cnid_t cnid_dbd_rebuild_add(struct _cnid_db *, const struct stat *,
+                                   const cnid_t, char *, const size_t, cnid_t);
 
 /* FIXME: These functions could be static in cnid_dbd.c */
 
index 608996b28aa3565a94952f2dc5bba680bf0a29d6..0f887e45ec40b5d3d513f249a02002e219b68a43 100644 (file)
@@ -63,7 +63,7 @@ pselect(int count, fd_set * restrict rfds, fd_set * restrict wfds,
         tvp = 0;
 
     if (mask != 0) {
-        rv = sigprocmask(SIG_SETMASK, mask, &omask);
+        rv = pthread_sigmask(SIG_SETMASK, mask, &omask);
         if (rv != 0)
             return rv;
     }
@@ -71,7 +71,7 @@ pselect(int count, fd_set * restrict rfds, fd_set * restrict wfds,
     rv = select(count, rfds, wfds, efds, tvp);
     if (mask != 0) {
         sverrno = errno;
-        sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0);
+        pthread_sigmask(SIG_SETMASK, &omask, (sigset_t *)0);
         errno = sverrno;
     }
 
index f34b81d256e33abef514797f6150353befebdfba..a929097c82ca6fd62028ed2180ac2737bde85f5c 100644 (file)
@@ -24,8 +24,8 @@ functions which need their own buffers: dsi_attention, dsi_tickle
 PERFORMANCE TWEAKING:
 sending complete packets or the header and a partial packet to the
 client should always be handled by proto_send. for dsi_tcp.c,
-proto_send will coalesce the header and data by using writev if
-USE_WRITEV is defined. in addition, appleshare sessions often involve
+proto_send will coalesce the header and data by using writev.
+in addition, appleshare sessions often involve
 the sending and receiving of many small packets. as a consequence, i
 use TCP_NODELAY to speed up the turnaround time.
 
index 8a9eaddedaac355cf484fc997ead9973da3311b8..9aef79b53534aeabd9598c622c001b7b8f4904bc 100644 (file)
@@ -34,7 +34,7 @@ int dsi_attention(DSI *dsi, AFPUserBytes flags)
   u_int32_t len, nlen;
   u_int16_t id;
 
-  if (dsi->asleep)
+  if (dsi->flags & DSI_SLEEPING)
       return 1;
 
   if (dsi->in_write) {
index 23c9cf46ce9898faf04b5b0a3dd19fe891d300b3..6362182ba49fe6d259dd67277eb8cc0f7df6b7e3 100644 (file)
@@ -17,7 +17,7 @@
 void dsi_close(DSI *dsi)
 {
   /* server generated. need to set all the fields. */
-  if (!dsi->asleep) {
+  if (!(dsi->flags & DSI_SLEEPING) && !(dsi->flags & DSI_DISCONNECTED)) {
       dsi->header.dsi_flags = DSIFL_REQUEST;
       dsi->header.dsi_command = DSIFUNC_CLOSE;
       dsi->header.dsi_requestID = htons(dsi_serverID(dsi));
index bde7318f28a6e7baa86d5868a3aa5ca03f6c5ffd..221ac47af76d50f4a01f777d8e17979207d1d578 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#ifdef HAVE_UNISTD_H
 #include <unistd.h>
-#endif /* HAVE_UNISTD_H */
 #include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
 
 /* POSIX.1 sys/wait.h check */
 #include <sys/types.h>
 #include <atalk/dsi.h>
 #include <atalk/server_child.h>
 
-static server_child *children = NULL;
-
-void dsi_kill(int sig)
-{
-  if (children)
-    server_child_kill(children, CHILD_DSIFORK, sig);
-}
-
 /* hand off the command. return child connection to the main program */
-DSI *dsi_getsession(DSI *dsi, server_child *serv_children, 
-                   const int tickleval)
+afp_child_t *dsi_getsession(DSI *dsi, server_child *serv_children, int tickleval)
 {
   pid_t pid;
-  
-  /* do a couple things on first entry */
-  if (!dsi->inited) {
-    if (!(children = serv_children))
-      return NULL;
-    dsi->inited = 1;
+  unsigned int ipc_fds[2];  
+  afp_child_t *child;
+
+  if (socketpair(PF_UNIX, SOCK_STREAM, 0, ipc_fds) < 0) {
+      LOG(log_error, logtype_dsi, "dsi_getsess: %s", strerror(errno));
+      exit( EXITERR_CLNT );
   }
-  
-  switch (pid = dsi->proto_open(dsi)) {
+
+  if (setnonblock(ipc_fds[0], 1) != 0 || setnonblock(ipc_fds[1], 1) != 0) {
+      LOG(log_error, logtype_dsi, "dsi_getsess: setnonblock: %s", strerror(errno));
+      exit(EXITERR_CLNT);
+  }
+
+  switch (pid = dsi->proto_open(dsi)) { /* in libatalk/dsi/dsi_tcp.c */
   case -1:
     /* if we fail, just return. it might work later */
     LOG(log_error, logtype_dsi, "dsi_getsess: %s", strerror(errno));
-    return dsi;
+    return NULL;
 
   case 0: /* child. mostly handled below. */
-    dsi->child = 1;
     break;
 
   default: /* parent */
     /* using SIGQUIT is hokey, but the child might not have
      * re-established its signal handler for SIGTERM yet. */
-    if (server_child_add(children, CHILD_DSIFORK, pid) < 0) {
+    if ((child = server_child_add(serv_children, CHILD_DSIFORK, pid, ipc_fds)) < 0) {
       LOG(log_error, logtype_dsi, "dsi_getsess: %s", strerror(errno));
       dsi->header.dsi_flags = DSIFL_REPLY;
       dsi->header.dsi_code = DSIERR_SERVBUSY;
@@ -79,14 +74,13 @@ DSI *dsi_getsession(DSI *dsi, server_child *serv_children,
       dsi->header.dsi_code = DSIERR_OK;
       kill(pid, SIGQUIT);
     }
-
     dsi->proto_close(dsi);
-    return dsi;
+    return child;
   }
   
   /* child: check number of open connections. this is one off the
    * actual count. */
-  if ((children->count >= children->nsessions) &&
+  if ((serv_children->count >= serv_children->nsessions) &&
       (dsi->header.dsi_command == DSIFUNC_OPEN)) {
     LOG(log_info, logtype_dsi, "dsi_getsess: too many connections");
     dsi->header.dsi_flags = DSIFL_REPLY;
@@ -97,8 +91,7 @@ DSI *dsi_getsession(DSI *dsi, server_child *serv_children,
 
   /* get rid of some stuff */
   close(dsi->serversock);
-  server_child_free(children); 
-  children = NULL;
+  server_child_free(serv_children); 
 
   switch (dsi->header.dsi_command) {
   case DSIFUNC_STAT: /* send off status and return */
@@ -129,7 +122,10 @@ DSI *dsi_getsession(DSI *dsi, server_child *serv_children,
     dsi->timer.it_interval.tv_usec = dsi->timer.it_value.tv_usec = 0;
     signal(SIGPIPE, SIG_IGN); /* we catch these ourselves */
     dsi_opensession(dsi);
-    return dsi;
+    if ((child = calloc(1, sizeof(afp_child_t))) == NULL)
+        exit(EXITERR_SYS);
+    child->ipc_fds[1] = ipc_fds[1];
+    return child;
     break;
 
   default: /* just close */
index 92b5029495066375868e1fe9db6148070ec840ba..2d7fd23b84c947789f060dfe7b89b3e3ab41f398 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: dsi_opensess.c,v 1.4 2005-09-07 15:27:29 didg Exp $
- *
  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
  * All rights reserved. See COPYRIGHT.
  */
 #include <stdio.h>
 #include <string.h>
 #include <sys/types.h>
+#include <stdlib.h>
 
 #include <atalk/dsi.h>
+#include <atalk/util.h>
+#include <atalk/logger.h>
+
+static void dsi_init_buffer(DSI *dsi)
+{
+    size_t quantum = dsi->server_quantum ? dsi->server_quantum : DSI_SERVQUANT_DEF;
+
+    /* default is 12 * 300k = 3,6 MB (Apr 2011) */
+    if ((dsi->buffer = malloc(dsi->dsireadbuf * quantum)) == NULL) {
+        LOG(log_error, logtype_dsi, "dsi_init_buffer: OOM");
+        AFP_PANIC("OOM in dsi_init_buffer");
+    }
+    dsi->start = dsi->buffer;
+    dsi->eof = dsi->buffer;
+    dsi->end = dsi->buffer + (dsi->dsireadbuf * quantum);
+}
 
 /* OpenSession. set up the connection */
 void dsi_opensession(DSI *dsi)
 {
   u_int32_t i = 0; /* this serves double duty. it must be 4-bytes long */
+  int offs;
+
+  dsi_init_buffer(dsi);
+  if (setnonblock(dsi->socket, 1) < 0) {
+      LOG(log_error, logtype_dsi, "dsi_opensession: setnonblock: %s", strerror(errno));
+      AFP_PANIC("setnonblock error");
+  }
 
   /* parse options */
   while (i < dsi->cmdlen) {
@@ -39,7 +61,10 @@ void dsi_opensession(DSI *dsi)
   dsi->header.dsi_flags = DSIFL_REPLY;
   dsi->header.dsi_code = 0;
   /* dsi->header.dsi_command = DSIFUNC_OPEN;*/
-  dsi->cmdlen = 2 + sizeof(i); /* length of data. dsi_send uses it. */
+
+  dsi->cmdlen = 2 * (2 + sizeof(i)); /* length of data. dsi_send uses it. */
+
+  /* DSI Option Server Request Quantum */
   dsi->commands[0] = DSIOPT_SERVQUANT;
   dsi->commands[1] = sizeof(i);
   i = htonl(( dsi->server_quantum < DSI_SERVQUANT_MIN || 
@@ -47,5 +72,11 @@ void dsi_opensession(DSI *dsi)
            DSI_SERVQUANT_DEF : dsi->server_quantum);
   memcpy(dsi->commands + 2, &i, sizeof(i));
 
+  /* AFP replaycache size option */
+  offs = 2 + sizeof(i);
+  dsi->commands[offs] = DSIOPT_REPLCSIZE;
+  dsi->commands[offs+1] = sizeof(i);
+  i = htonl(REPLAYCACHE_SIZE);
+  memcpy(dsi->commands + offs + 2, &i, sizeof(i));
   dsi_send(dsi);
 }
index 55345ba868738d20ee798ab5ce58e33f1bd69934..fc86c0a1339944d011cecc7c10b8fbbbcc88ea44 100644 (file)
@@ -36,7 +36,7 @@ ssize_t dsi_readinit(DSI *dsi, void *buf, const size_t buflen,
                    const size_t size, const int err)
 {
 
-  dsi->noreply = 1; /* we will handle our own replies */
+  dsi->flags |= DSI_NOREPLY; /* we will handle our own replies */
   dsi->header.dsi_flags = DSIFL_REPLY;
   /*dsi->header.dsi_command = DSIFUNC_CMD;*/
   dsi->header.dsi_len = htonl(size);
index ee595bbd3274a80ab02a09c70a7a4d215f80b3c4..680967e8be1f543b833d6ae9e6c6caf70d59f532 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: dsi_stream.c,v 1.20 2009-10-26 12:35:56 franklahm Exp $
- *
  * Copyright (c) 1998 Adrian Sun (asun@zoology.washington.edu)
  * All rights reserved. See COPYRIGHT.
  *
@@ -15,8 +13,6 @@
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
 
-#define USE_WRITEV
-
 #include <stdio.h>
 #include <stdlib.h>
 
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/socket.h>
-
-#ifdef USE_WRITEV
 #include <sys/uio.h>
-#endif
 
 #include <atalk/logger.h>
 #include <atalk/dsi.h>
 #define MSG_DONTWAIT 0x40
 #endif
 
-/* ------------------------- 
- * we don't use a circular buffer.
-*/
-static void dsi_init_buffer(DSI *dsi)
-{
-    if (!dsi->buffer) {
-        /* XXX config options */
-        dsi->maxsize = 6 * dsi->server_quantum;
-        if (!dsi->maxsize)
-            dsi->maxsize = 6 * DSI_SERVQUANT_DEF;
-        dsi->buffer = malloc(dsi->maxsize);
-        if (!dsi->buffer) {
-            return;
-        }
-        dsi->start = dsi->buffer;
-        dsi->eof = dsi->buffer;
-        dsi->end = dsi->buffer + dsi->maxsize;
-    }
-}
-
-/* ---------------------- 
-   afpd is sleeping too much while trying to send something.
-   May be there's no reader or the reader is also sleeping in write,
-   look if there's some data for us to read, hopefully it will wake up
-   the reader so we can write again.
-*/
+/*
+ * afpd is sleeping too much while trying to send something.
+ * May be there's no reader or the reader is also sleeping in write,
+ * look if there's some data for us to read, hopefully it will wake up
+ * the reader so we can write again.
+ *
+ * @returns 0 when is possible to send again, -1 on error
+ */
 static int dsi_peek(DSI *dsi)
 {
+    static int warned = 0;
     fd_set readfds, writefds;
     int    len;
     int    maxfd;
     int    ret;
 
-    FD_ZERO(&readfds);
-    FD_ZERO(&writefds);
-    FD_SET( dsi->socket, &readfds);
-    FD_SET( dsi->socket, &writefds);
-    maxfd = dsi->socket +1;
+    LOG(log_debug, logtype_dsi, "dsi_peek");
+
+    maxfd = dsi->socket + 1;
 
     while (1) {
-        FD_SET( dsi->socket, &readfds);
+        FD_ZERO(&readfds);
+        FD_ZERO(&writefds);
+
+        if (dsi->eof < dsi->end) {
+            /* space in read buffer */
+            FD_SET( dsi->socket, &readfds);
+        } else {
+            if (!warned) {
+                warned = 1;
+                LOG(log_note, logtype_dsi, "dsi_peek: readahead buffer is full, possibly increase -dsireadbuf option");
+                LOG(log_note, logtype_dsi, "dsi_peek: dsireadbuf: %d, DSI quantum: %d, effective buffer size: %d",
+                    dsi->dsireadbuf,
+                    dsi->server_quantum ? dsi->server_quantum : DSI_SERVQUANT_DEF,
+                    dsi->end - dsi->buffer);
+            }
+        }
+
         FD_SET( dsi->socket, &writefds);
 
         /* No timeout: if there's nothing to read nor nothing to write,
@@ -98,29 +88,35 @@ static int dsi_peek(DSI *dsi)
                 /* we might have been interrupted by out timer, so restart select */
                 continue;
             /* give up */
+            LOG(log_error, logtype_dsi, "dsi_peek: unexpected select return: %d %s",
+                ret, ret < 0 ? strerror(errno) : "");
+            return -1;
+        }
+
+        if (FD_ISSET(dsi->socket, &writefds)) {
+            /* we can write again */
+            LOG(log_debug, logtype_dsi, "dsi_peek: can write again");
             break;
         }
 
         /* Check if there's sth to read, hopefully reading that will unblock the client */
         if (FD_ISSET(dsi->socket, &readfds)) {
-            dsi_init_buffer(dsi);
-            len = dsi->end - dsi->eof;
-
-            if (len <= 0) {
-                /* ouch, our buffer is full ! fall back to blocking IO 
-                 * could block and disconnect but it's better than a cpu hog */
-                break;
+            len = dsi->end - dsi->eof; /* it's ensured above that there's space */
+
+            if ((len = read(dsi->socket, dsi->eof, len)) <= 0) {
+                if (len == 0) {
+                    LOG(log_error, logtype_dsi, "dsi_peek: EOF");
+                    return -1;
+                }
+                LOG(log_error, logtype_dsi, "dsi_peek: read: %s", strerror(errno));
+                if (errno == EAGAIN)
+                    continue;
+                return -1;
             }
+            LOG(log_debug, logtype_dsi, "dsi_peek: read %d bytes", len);
 
-            len = read(dsi->socket, dsi->eof, len);
-            if (len <= 0)
-                break;
             dsi->eof += len;
         }
-
-        if (FD_ISSET(dsi->socket, &writefds))
-            /* we can write again at last */
-            break;
     }
 
     return 0;
@@ -141,12 +137,6 @@ ssize_t dsi_stream_write(DSI *dsi, void *data, const size_t length, int mode)
 
   LOG(log_maxdebug, logtype_dsi, "dsi_stream_write: sending %u bytes", length);
 
-  /* non blocking mode */
-  if (setnonblock(dsi->socket, 1) < 0) {
-      LOG(log_error, logtype_dsi, "dsi_stream_write: setnonblock: %s", strerror(errno));
-      return -1;
-  }
-
   while (written < length) {
       len = send(dsi->socket, (u_int8_t *) data + written, length - written, flags);
       if (len >= 0) {
@@ -158,6 +148,8 @@ ssize_t dsi_stream_write(DSI *dsi, void *data, const size_t length, int mode)
           continue;
 
       if (errno == EAGAIN || errno == EWOULDBLOCK) {
+          LOG(log_debug, logtype_dsi, "dsi_stream_write: send: %s", strerror(errno));
+
           if (mode == DSI_NOWAIT && written == 0) {
               /* DSI_NOWAIT is used by attention give up in this case. */
               written = -1;
@@ -181,11 +173,6 @@ ssize_t dsi_stream_write(DSI *dsi, void *data, const size_t length, int mode)
   dsi->write_count += written;
 
 exit:
-  if (setnonblock(dsi->socket, 0) < 0) {
-      LOG(log_error, logtype_dsi, "dsi_stream_write: setnonblock: %s", strerror(errno));
-      written = -1;
-  }
-
   dsi->in_write--;
   return written;
 }
@@ -199,15 +186,11 @@ ssize_t dsi_stream_read_file(DSI *dsi, int fromfd, off_t offset, const size_t le
   size_t written;
   ssize_t len;
 
+  LOG(log_maxdebug, logtype_dsi, "dsi_stream_read_file: sending %u bytes", length);
+
   dsi->in_write++;
   written = 0;
 
-  /* non blocking mode */
-  if (setnonblock(dsi->socket, 1) < 0) {
-      LOG(log_error, logtype_dsi, "dsi_stream_read_file: setnonblock: %s", strerror(errno));
-      return -1;
-  }
-
   while (written < length) {
     len = sys_sendfile(dsi->socket, fromfd, &offset, length - written);
         
@@ -226,7 +209,7 @@ ssize_t dsi_stream_read_file(DSI *dsi, int fromfd, off_t offset, const size_t le
           }
           continue;
       }
-      LOG(log_error, logtype_dsi, "dsi_stream_write: %s", strerror(errno));
+      LOG(log_error, logtype_dsi, "dsi_stream_read_file: %s", strerror(errno));
       break;
     }
     else if (!len) {
@@ -238,11 +221,6 @@ ssize_t dsi_stream_read_file(DSI *dsi, int fromfd, off_t offset, const size_t le
         written += len;
   }
 
-  if (setnonblock(dsi->socket, 0) < 0) {
-      LOG(log_error, logtype_dsi, "dsi_stream_read_file: setnonblock: %s", strerror(errno));
-      return -1;
-  }
-
   dsi->write_count += written;
   dsi->in_write--;
   return written;
@@ -255,20 +233,27 @@ ssize_t dsi_stream_read_file(DSI *dsi, int fromfd, off_t offset, const size_t le
 static size_t from_buf(DSI *dsi, u_int8_t *buf, size_t count)
 {
     size_t nbe = 0;
-    
-    if (dsi->start) {        
-        nbe = dsi->eof - dsi->start;
 
-        if (nbe > 0) {
-           nbe = min((size_t)nbe, count);
-           memcpy(buf, dsi->start, nbe);
-           dsi->start += nbe;
+    if (dsi->buffer == NULL)
+        /* afpd master has no DSI buffering */
+        return 0;
+
+    LOG(log_maxdebug, logtype_dsi, "from_buf: %u bytes", count);
+    
+    nbe = dsi->eof - dsi->start;
 
-           if (dsi->eof == dsi->start) 
-               dsi->start = dsi->eof = dsi->buffer;
+    if (nbe > 0) {
+        nbe = min((size_t)nbe, count);
+        memcpy(buf, dsi->start, nbe);
+        dsi->start += nbe;
 
-        }
+        if (dsi->eof == dsi->start)
+            dsi->start = dsi->eof = dsi->buffer;
     }
+
+    LOG(log_debug, logtype_dsi, "from_buf(read: %u, unread:%u , space left: %u): returning %u",
+        dsi->start - dsi->buffer, dsi->eof - dsi->start, dsi->end - dsi->eof, nbe);
+
     return nbe;
 }
 
@@ -282,44 +267,61 @@ static size_t from_buf(DSI *dsi, u_int8_t *buf, size_t count)
  */
 static ssize_t buf_read(DSI *dsi, u_int8_t *buf, size_t count)
 {
-    ssize_t nbe;
-    
+    ssize_t len;
+
+    LOG(log_maxdebug, logtype_dsi, "buf_read(%u bytes)", count);
+
     if (!count)
         return 0;
 
-    nbe = from_buf(dsi, buf, count); /* 1. */
-    if (nbe)
-        return nbe;             /* 2. */
+    len = from_buf(dsi, buf, count); /* 1. */
+    if (len)
+        return len;             /* 2. */
   
-    return read(dsi->socket, buf, count); /* 3. */
+    len = readt(dsi->socket, buf, count, 0, 1); /* 3. */
+
+    LOG(log_maxdebug, logtype_dsi, "buf_read(%u bytes): got: %d", count, len);
+
+    return len;
 }
 
 /*
  * Essentially a loop around buf_read() to ensure "length" bytes are read
  * from dsi->buffer and/or the socket.
+ *
+ * @returns length on success, some value smaller then length indicates an error
  */
 size_t dsi_stream_read(DSI *dsi, void *data, const size_t length)
 {
   size_t stored;
   ssize_t len;
-  
+
+  LOG(log_maxdebug, logtype_dsi, "dsi_stream_read(%u bytes)", length);
+
   stored = 0;
   while (stored < length) {
-    len = buf_read(dsi, (u_int8_t *) data + stored, length - stored);
-    if (len == -1 && errno == EINTR)
-      continue;
-    else if (len > 0)
-      stored += len;
-    else { /* eof or error */
-      /* don't log EOF error if it's just after connect (OSX 10.3 probe) */
-      if (len || stored || dsi->read_count) {
-          LOG(log_error, logtype_dsi, "dsi_stream_read(%d): %s", len, (len < 0)?strerror(errno):"unexpected EOF");
+      len = buf_read(dsi, (u_int8_t *) data + stored, length - stored);
+      if (len == -1 && (errno == EINTR || errno == EAGAIN)) {
+          LOG(log_debug, logtype_dsi, "dsi_stream_read: select read loop");
+          continue;
+      } else if (len > 0) {
+          stored += len;
+      } else { /* eof or error */
+          /* don't log EOF error if it's just after connect (OSX 10.3 probe) */
+          if (len || stored || dsi->read_count) {
+              if (! (dsi->flags & DSI_DISCONNECTED)) {
+                  LOG(log_error, logtype_dsi, "dsi_stream_read: len:%d, %s",
+                      len, (len < 0) ? strerror(errno) : "unexpected EOF");
+              }
+              return 0;
+          }
+          break;
       }
-      break;
-    }
   }
 
   dsi->read_count += stored;
+
+  LOG(log_maxdebug, logtype_dsi, "dsi_stream_read(%u bytes): got: %u", length, stored);
   return stored;
 }
 
@@ -331,8 +333,9 @@ static size_t dsi_buffered_stream_read(DSI *dsi, u_int8_t *data, const size_t le
 {
   size_t len;
   size_t buflen;
+
+  LOG(log_maxdebug, logtype_dsi, "dsi_buffered_stream_read: %u bytes", length);
   
-  dsi_init_buffer(dsi);
   len = from_buf(dsi, data, length); /* read from buffer dsi->buffer */
   dsi->read_count += len;
   if (len == length) {          /* got enough bytes from there ? */
@@ -353,13 +356,6 @@ static size_t dsi_buffered_stream_read(DSI *dsi, u_int8_t *data, const size_t le
   return len;
 }
 
-/* ---------------------------------------
-*/
-void dsi_sleep(DSI *dsi, const int state)
-{
-    dsi->asleep = state;
-}
-
 /* ---------------------------------------
 */
 static void block_sig(DSI *dsi)
@@ -381,11 +377,12 @@ static void unblock_sig(DSI *dsi)
 int dsi_stream_send(DSI *dsi, void *buf, size_t length)
 {
   char block[DSI_BLOCKSIZ];
-#ifdef USE_WRITEV
   struct iovec iov[2];
   size_t towrite;
   ssize_t len;
-#endif /* USE_WRITEV */
+
+  LOG(log_maxdebug, logtype_dsi, "dsi_stream_send: %u bytes",
+      length ? length : sizeof(block));
 
   block[0] = dsi->header.dsi_flags;
   block[1] = dsi->header.dsi_command;
@@ -403,7 +400,6 @@ int dsi_stream_send(DSI *dsi, void *buf, size_t length)
   
   /* block signals */
   block_sig(dsi);
-#ifdef USE_WRITEV
   iov[0].iov_base = block;
   iov[0].iov_len = sizeof(block);
   iov[1].iov_base = buf;
@@ -412,46 +408,36 @@ int dsi_stream_send(DSI *dsi, void *buf, size_t length)
   towrite = sizeof(block) + length;
   dsi->write_count += towrite;
   while (towrite > 0) {
-    if (((len = writev(dsi->socket, iov, 2)) == -1 && errno == EINTR) || 
-       !len)
-      continue;
+      if (((len = writev(dsi->socket, iov, 2)) == -1 && errno == EINTR) || (len == 0))
+          continue;
     
-    if ((size_t)len == towrite) /* wrote everything out */
-      break;
-    else if (len < 0) { /* error */
-      if (errno == EAGAIN || errno == EWOULDBLOCK) {
-          if (!dsi_peek(dsi)) {
-              continue;
+      if ((size_t)len == towrite) /* wrote everything out */
+          break;
+      else if (len < 0) { /* error */
+          if (errno == EAGAIN || errno == EWOULDBLOCK) {
+              if (!dsi_peek(dsi)) {
+                  continue;
+              }
           }
+          LOG(log_error, logtype_dsi, "dsi_stream_send: %s", strerror(errno));
+          unblock_sig(dsi);
+          return 0;
       }
-      LOG(log_error, logtype_dsi, "dsi_stream_send: %s", strerror(errno));
-      unblock_sig(dsi);
-      return 0;
-    }
     
-    towrite -= len;
-    if (towrite > length) { /* skip part of header */
-      iov[0].iov_base = (char *) iov[0].iov_base + len;
-      iov[0].iov_len -= len;
-    } else { /* skip to data */
-      if (iov[0].iov_len) {
-       len -= iov[0].iov_len;
-       iov[0].iov_len = 0;
+      towrite -= len;
+      if (towrite > length) { /* skip part of header */
+          iov[0].iov_base = (char *) iov[0].iov_base + len;
+          iov[0].iov_len -= len;
+      } else { /* skip to data */
+          if (iov[0].iov_len) {
+              len -= iov[0].iov_len;
+              iov[0].iov_len = 0;
+          }
+          iov[1].iov_base = (char *) iov[1].iov_base + len;
+          iov[1].iov_len -= len;
       }
-      iov[1].iov_base = (char *) iov[1].iov_base + len;
-      iov[1].iov_len -= len;
-    }
   }
   
-#else /* USE_WRITEV */
-  /* write the header then data */
-  if ((dsi_stream_write(dsi, block, sizeof(block), 1) != sizeof(block)) ||
-            (dsi_stream_write(dsi, buf, length, 0) != length)) {
-      unblock_sig(dsi);
-      return 0;
-  }
-#endif /* USE_WRITEV */
-
   unblock_sig(dsi);
   return 1;
 }
@@ -466,6 +452,8 @@ int dsi_stream_receive(DSI *dsi, void *buf, const size_t ilength,
 {
   char block[DSI_BLOCKSIZ];
 
+  LOG(log_maxdebug, logtype_dsi, "dsi_stream_receive: %u bytes", ilength);
+
   /* read in the header */
   if (dsi_buffered_stream_read(dsi, (u_int8_t *)block, sizeof(block)) != sizeof(block)) 
     return 0;
index 243003b5aac5c495a886fc0453161d9c377b49fd..0c60303ecb6159c5310d44d28f59242876e69e2d 100644 (file)
@@ -127,6 +127,9 @@ static int dsi_tcp_open(DSI *dsi)
         u_int8_t block[DSI_BLOCKSIZ];
         size_t stored;
 
+        /* Immediateyl mark globally that we're a child now */
+        parent_or_child = 1;
+
         /* reset signals */
         server_reset_signal();
 
@@ -218,7 +221,7 @@ static int dsi_tcp_open(DSI *dsi)
 #define IFF_SLAVE 0
 #endif
 
-static void guess_interface(DSI *dsi, const char *hostname)
+static void guess_interface(DSI *dsi, const char *hostname, const char *port)
 {
     int fd;
     char **start, **list;
@@ -250,11 +253,11 @@ static void guess_interface(DSI *dsi, const char *hostname)
 
         memset(&dsi->server, 0, sizeof(struct sockaddr_storage));
         sa->sin_family = AF_INET;
-        sa->sin_port = htons(548);
+        sa->sin_port = htons(atoi(port));
         sa->sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
 
-        LOG(log_info, logtype_dsi, "dsi_tcp: '%s' on interface '%s' will be used instead.",
-                  getip_string((struct sockaddr *)&dsi->server), ifr.ifr_name);
+        LOG(log_info, logtype_dsi, "dsi_tcp: '%s:%s' on interface '%s' will be used instead.",
+            getip_string((struct sockaddr *)&dsi->server), port, ifr.ifr_name);
         goto iflist_done;
     }
     LOG(log_info, logtype_dsi, "dsi_tcp (Chooser will not select afp/tcp) "
@@ -283,14 +286,23 @@ int dsi_tcp_init(DSI *dsi, const char *hostname, const char *address,
 
     /* Prepare hint for getaddrinfo */
     memset(&hints, 0, sizeof hints);
+#if !defined(FREEBSD)
     hints.ai_family = AF_UNSPEC;
+#endif
     hints.ai_socktype = SOCK_STREAM;
     hints.ai_flags = AI_NUMERICSERV;
-    if ( ! address)
+
+    if ( ! address) {
         hints.ai_flags |= AI_PASSIVE;
-    else
+#if defined(FREEBSD)
+        hints.ai_family = AF_INET6;
+#endif
+    } else {
         hints.ai_flags |= AI_NUMERICHOST;
-
+#if defined(FREEBSD)
+        hints.ai_family = AF_UNSPEC;
+#endif
+    }
     if ((ret = getaddrinfo(address ? address : NULL, port ? port : "548", &hints, &servinfo)) != 0) {
         LOG(log_error, logtype_dsi, "dsi_tcp_init: getaddrinfo: %s\n", gai_strerror(ret));
         return 0;
@@ -316,6 +328,10 @@ int dsi_tcp_init(DSI *dsi, const char *hostname, const char *address,
             flag = 1;
             setsockopt(dsi->serversock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
 #endif
+#if defined(FREEBSD) && defined(IPV6_BINDV6ONLY)
+            int on = 0;
+            setsockopt(dsi->serversock, IPPROTO_IPV6, IPV6_BINDV6ONLY, (char *)&on, sizeof (on));
+#endif
 
 #ifdef USE_TCP_NODELAY
 #ifndef SOL_TCP
@@ -395,7 +411,7 @@ int dsi_tcp_init(DSI *dsi, const char *hostname, const char *address,
     freeaddrinfo(servinfo);
 
 interfaces:
-    guess_interface(dsi, hostname);
+    guess_interface(dsi, hostname, port ? port : "548");
     return 1;
 }
 
index 02aa0536577e2126ad64256b80f0d3e079ceb69b..2a996539a968ba0201c8c19d1f62e9a53a8f7c25 100644 (file)
@@ -23,9 +23,8 @@ int dsi_tickle(DSI *dsi)
 {
   char block[DSI_BLOCKSIZ];
   u_int16_t id;
-  int ret;
   
-  if (dsi->asleep || dsi->in_write)
+  if ((dsi->flags & DSI_SLEEPING) || dsi->in_write)
       return 1;
 
   id = htons(dsi_serverID(dsi));
@@ -36,11 +35,6 @@ int dsi_tickle(DSI *dsi)
   memcpy(block + 2, &id, sizeof(id));
   /* code = len = reserved = 0 */
 
-  ret = dsi_stream_write(dsi, block, DSI_BLOCKSIZ, DSI_NOWAIT);
-  /* we don't really care if we can't send a tickle, it will fail
-   * elsewhere
-  */
-  ret = (ret == -1 || ret == DSI_BLOCKSIZ);
-  return ret;
+  return dsi_stream_write(dsi, block, DSI_BLOCKSIZ, DSI_NOWAIT);
 }
 
index 7e49b1630554aeee5b1aa3529f3ae9daceeb009f..90f8ed5209e762a23044ac1dda5217896e70e734 100644 (file)
@@ -5,4 +5,4 @@ Makefile.in
 .deps
 .libs
 .gitignore
-charcnv.o iconv.o utf8.o util_unistr.o
+charcnv.o iconv.o utf16_case.o utf8.o util_unistr.o
index a697dfcdf966a4857fad0965ada03d1f37358124..b56f9f354d754e6fde0c155562bd8536fa7ca1f4 100644 (file)
@@ -12,10 +12,11 @@ libunicode_la_SOURCES = \
        util_unistr.c   \
        iconv.c         \
        charcnv.c       \
-       utf8.c
+       utf8.c          \
+       utf16_case.c
 
 libunicode_la_LIBADD = $(LIBUNICODE_DEPS)
 
-noinst_HEADERS = ucs2_casetable.h precompose.h byteorder.h
+noinst_HEADERS = utf16_casetable.h precompose.h byteorder.h
 
 LIBS=@ICONV_LIBS@
index 9eba06920d19e7b651f9e794c19a77049152b70e..c925b00d74204a8792e911a43a30332134f17aca 100644 (file)
@@ -1,9 +1,30 @@
-/* This file is generated by contrib/misc/make-precompose.h.pl UnicodeData.txt */
 /* DO NOT EDIT BY HAND!!!                                           */
+/* This file is generated by                                        */
+/*              contrib/misc/make-precompose.h.pl UnicodeData.txt   */
 
 /* UnicodeData.txt is got from                                      */
 /* http://www.unicode.org/Public/UNIDATA/UnicodeData.txt            */
 
+#define SBASE 0xAC00
+#define LBASE 0x1100
+#define VBASE 0x1161
+#define TBASE 0x11A7
+#define LCOUNT 19
+#define VCOUNT 21
+#define TCOUNT 28
+#define NCOUNT 588     /* (VCOUNT * TCOUNT) */
+#define SCOUNT 11172   /* (LCOUNT * NCOUNT) */
+
+#define PRECOMP_COUNT 955
+#define DECOMP_COUNT 955
+#define MAXCOMBLEN 3
+
+#define PRECOMP_SP_COUNT 16
+#define DECOMP_SP_COUNT 16
+#define MAXCOMBSPLEN 4
+
+#define COMBBUFLEN 4  /* max(MAXCOMBLEN,MAXCOMBSPLEN) */
+
 static const struct {
   unsigned int replacement;
   unsigned int base;
@@ -1009,6 +1030,9 @@ static const struct {
   { 0x000030FE, 0x000030FD, 0x00003099 },     /* KATAKANA VOICED ITERATION MARK */
   { 0x0000FB2C, 0x0000FB49, 0x000005C1 },     /* HEBREW LETTER SHIN WITH DAGESH AND SHIN DOT */
   { 0x0000FB2D, 0x0000FB49, 0x000005C2 },     /* HEBREW LETTER SHIN WITH DAGESH AND SIN DOT */
+/*{ 0x0001109A, 0x00011099, 0x000110BA },*/   /* KAITHI LETTER DDDHA */
+/*{ 0x0001109C, 0x0001109B, 0x000110BA },*/   /* KAITHI LETTER RHA */
+/*{ 0x000110AB, 0x000110A5, 0x000110BA },*/   /* KAITHI LETTER VA */
 /*{ 0x0001D15E, 0x0001D157, 0x0001D165 },*/   /* MUSICAL SYMBOL HALF NOTE */
 /*{ 0x0001D15F, 0x0001D158, 0x0001D165 },*/   /* MUSICAL SYMBOL QUARTER NOTE */
 /*{ 0x0001D160, 0x0001D15F, 0x0001D16E },*/   /* MUSICAL SYMBOL EIGHTH NOTE */
@@ -2029,6 +2053,9 @@ static const struct {
   { 0x0000FB4C, 0x000005D1, 0x000005BF },     /* HEBREW LETTER BET WITH RAFE */
   { 0x0000FB4D, 0x000005DB, 0x000005BF },     /* HEBREW LETTER KAF WITH RAFE */
   { 0x0000FB4E, 0x000005E4, 0x000005BF },     /* HEBREW LETTER PE WITH RAFE */
+/*{ 0x0001109A, 0x00011099, 0x000110BA },*/   /* KAITHI LETTER DDDHA */
+/*{ 0x0001109C, 0x0001109B, 0x000110BA },*/   /* KAITHI LETTER RHA */
+/*{ 0x000110AB, 0x000110A5, 0x000110BA },*/   /* KAITHI LETTER VA */
 /*{ 0x0001D15E, 0x0001D157, 0x0001D165 },*/   /* MUSICAL SYMBOL HALF NOTE */
 /*{ 0x0001D15F, 0x0001D158, 0x0001D165 },*/   /* MUSICAL SYMBOL QUARTER NOTE */
 /*{ 0x0001D160, 0x0001D15F, 0x0001D16E },*/   /* MUSICAL SYMBOL EIGHTH NOTE */
@@ -2044,4 +2071,50 @@ static const struct {
 /*{ 0x0001D1C0, 0x0001D1BC, 0x0001D16F },*/   /* MUSICAL SYMBOL FUSA BLACK */
 };
 
+static const struct {
+  unsigned int replacement_sp;
+  unsigned int base_sp;
+  unsigned int comb_sp;
+} precompositions_sp[] = {
+  { 0xD804DC9A, 0xD804DC99, 0xD804DCBA },     /* KAITHI LETTER DDDHA */
+  { 0xD804DC9C, 0xD804DC9B, 0xD804DCBA },     /* KAITHI LETTER RHA */
+  { 0xD804DCAB, 0xD804DCA5, 0xD804DCBA },     /* KAITHI LETTER VA */
+  { 0xD834DD5E, 0xD834DD57, 0xD834DD65 },     /* MUSICAL SYMBOL HALF NOTE */
+  { 0xD834DD5F, 0xD834DD58, 0xD834DD65 },     /* MUSICAL SYMBOL QUARTER NOTE */
+  { 0xD834DD60, 0xD834DD5F, 0xD834DD6E },     /* MUSICAL SYMBOL EIGHTH NOTE */
+  { 0xD834DD61, 0xD834DD5F, 0xD834DD6F },     /* MUSICAL SYMBOL SIXTEENTH NOTE */
+  { 0xD834DD62, 0xD834DD5F, 0xD834DD70 },     /* MUSICAL SYMBOL THIRTY-SECOND NOTE */
+  { 0xD834DD63, 0xD834DD5F, 0xD834DD71 },     /* MUSICAL SYMBOL SIXTY-FOURTH NOTE */
+  { 0xD834DD64, 0xD834DD5F, 0xD834DD72 },     /* MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE */
+  { 0xD834DDBB, 0xD834DDB9, 0xD834DD65 },     /* MUSICAL SYMBOL MINIMA */
+  { 0xD834DDBC, 0xD834DDBA, 0xD834DD65 },     /* MUSICAL SYMBOL MINIMA BLACK */
+  { 0xD834DDBD, 0xD834DDBB, 0xD834DD6E },     /* MUSICAL SYMBOL SEMIMINIMA WHITE */
+  { 0xD834DDBF, 0xD834DDBB, 0xD834DD6F },     /* MUSICAL SYMBOL FUSA WHITE */
+  { 0xD834DDBE, 0xD834DDBC, 0xD834DD6E },     /* MUSICAL SYMBOL SEMIMINIMA BLACK */
+  { 0xD834DDC0, 0xD834DDBC, 0xD834DD6F },     /* MUSICAL SYMBOL FUSA BLACK */
+};
+
+static const struct {
+  unsigned int replacement_sp;
+  unsigned int base_sp;
+  unsigned int comb_sp;
+} decompositions_sp[] = {
+  { 0xD804DC9A, 0xD804DC99, 0xD804DCBA },     /* KAITHI LETTER DDDHA */
+  { 0xD804DC9C, 0xD804DC9B, 0xD804DCBA },     /* KAITHI LETTER RHA */
+  { 0xD804DCAB, 0xD804DCA5, 0xD804DCBA },     /* KAITHI LETTER VA */
+  { 0xD834DD5E, 0xD834DD57, 0xD834DD65 },     /* MUSICAL SYMBOL HALF NOTE */
+  { 0xD834DD5F, 0xD834DD58, 0xD834DD65 },     /* MUSICAL SYMBOL QUARTER NOTE */
+  { 0xD834DD60, 0xD834DD5F, 0xD834DD6E },     /* MUSICAL SYMBOL EIGHTH NOTE */
+  { 0xD834DD61, 0xD834DD5F, 0xD834DD6F },     /* MUSICAL SYMBOL SIXTEENTH NOTE */
+  { 0xD834DD62, 0xD834DD5F, 0xD834DD70 },     /* MUSICAL SYMBOL THIRTY-SECOND NOTE */
+  { 0xD834DD63, 0xD834DD5F, 0xD834DD71 },     /* MUSICAL SYMBOL SIXTY-FOURTH NOTE */
+  { 0xD834DD64, 0xD834DD5F, 0xD834DD72 },     /* MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE */
+  { 0xD834DDBB, 0xD834DDB9, 0xD834DD65 },     /* MUSICAL SYMBOL MINIMA */
+  { 0xD834DDBC, 0xD834DDBA, 0xD834DD65 },     /* MUSICAL SYMBOL MINIMA BLACK */
+  { 0xD834DDBD, 0xD834DDBB, 0xD834DD6E },     /* MUSICAL SYMBOL SEMIMINIMA WHITE */
+  { 0xD834DDBE, 0xD834DDBC, 0xD834DD6E },     /* MUSICAL SYMBOL SEMIMINIMA BLACK */
+  { 0xD834DDBF, 0xD834DDBB, 0xD834DD6F },     /* MUSICAL SYMBOL FUSA WHITE */
+  { 0xD834DDC0, 0xD834DDBC, 0xD834DD6F },     /* MUSICAL SYMBOL FUSA BLACK */
+};
+
 /* EOF */
diff --git a/libatalk/unicode/ucs2_casetable.h b/libatalk/unicode/ucs2_casetable.h
deleted file mode 100644 (file)
index 8ac86da..0000000
+++ /dev/null
@@ -1,472 +0,0 @@
-static const u_int16_t upcase_table_1[64] = {
-0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,   /* 0x0040-0x0047 */ 
-0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,   /* 0x0048-0x004F */ 
-0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,   /* 0x0050-0x0057 */ 
-0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,   /* 0x0058-0x005F */ 
-0x0060, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,   /* 0x0060-0x0067 */ 
-0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,   /* 0x0068-0x006F */ 
-0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,   /* 0x0070-0x0077 */ 
-0x0058, 0x0059, 0x005A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F }; /* 0x0078-0x007F */
-
-static const u_int16_t upcase_table_2[512] = {
-0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,   /* 0x00C0-0x00C7 */ 
-0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,   /* 0x00C8-0x00CF */ 
-0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,   /* 0x00D0-0x00D7 */ 
-0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,   /* 0x00D8-0x00DF */ 
-0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,   /* 0x00E0-0x00E7 */ 
-0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,   /* 0x00E8-0x00EF */ 
-0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00F7,   /* 0x00F0-0x00F7 */ 
-0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x0178,   /* 0x00F8-0x00FF */ 
-0x0100, 0x0100, 0x0102, 0x0102, 0x0104, 0x0104, 0x0106, 0x0106,   /* 0x0100-0x0107 */ 
-0x0108, 0x0108, 0x010A, 0x010A, 0x010C, 0x010C, 0x010E, 0x010E,   /* 0x0108-0x010F */ 
-0x0110, 0x0110, 0x0112, 0x0112, 0x0114, 0x0114, 0x0116, 0x0116,   /* 0x0110-0x0117 */ 
-0x0118, 0x0118, 0x011A, 0x011A, 0x011C, 0x011C, 0x011E, 0x011E,   /* 0x0118-0x011F */ 
-0x0120, 0x0120, 0x0122, 0x0122, 0x0124, 0x0124, 0x0126, 0x0126,   /* 0x0120-0x0127 */ 
-0x0128, 0x0128, 0x012A, 0x012A, 0x012C, 0x012C, 0x012E, 0x012E,   /* 0x0128-0x012F */ 
-0x0130, 0x0131, 0x0132, 0x0132, 0x0134, 0x0134, 0x0136, 0x0136,   /* 0x0130-0x0137 */ 
-0x0138, 0x0139, 0x0139, 0x013B, 0x013B, 0x013D, 0x013D, 0x013F,   /* 0x0138-0x013F */ 
-0x013F, 0x0141, 0x0141, 0x0143, 0x0143, 0x0145, 0x0145, 0x0147,   /* 0x0140-0x0147 */ 
-0x0147, 0x0149, 0x014A, 0x014A, 0x014C, 0x014C, 0x014E, 0x014E,   /* 0x0148-0x014F */ 
-0x0150, 0x0150, 0x0152, 0x0152, 0x0154, 0x0154, 0x0156, 0x0156,   /* 0x0150-0x0157 */ 
-0x0158, 0x0158, 0x015A, 0x015A, 0x015C, 0x015C, 0x015E, 0x015E,   /* 0x0158-0x015F */ 
-0x0160, 0x0160, 0x0162, 0x0162, 0x0164, 0x0164, 0x0166, 0x0166,   /* 0x0160-0x0167 */ 
-0x0168, 0x0168, 0x016A, 0x016A, 0x016C, 0x016C, 0x016E, 0x016E,   /* 0x0168-0x016F */ 
-0x0170, 0x0170, 0x0172, 0x0172, 0x0174, 0x0174, 0x0176, 0x0176,   /* 0x0170-0x0177 */ 
-0x0178, 0x0179, 0x0179, 0x017B, 0x017B, 0x017D, 0x017D, 0x017F,   /* 0x0178-0x017F */ 
-0x0180, 0x0181, 0x0182, 0x0182, 0x0184, 0x0184, 0x0186, 0x0187,   /* 0x0180-0x0187 */ 
-0x0187, 0x0189, 0x018A, 0x018B, 0x018B, 0x018D, 0x018E, 0x018F,   /* 0x0188-0x018F */ 
-0x0190, 0x0191, 0x0191, 0x0193, 0x0194, 0x0195, 0x0196, 0x0197,   /* 0x0190-0x0197 */ 
-0x0198, 0x0198, 0x019A, 0x019B, 0x019C, 0x019D, 0x019E, 0x019F,   /* 0x0198-0x019F */ 
-0x01A0, 0x01A0, 0x01A2, 0x01A2, 0x01A4, 0x01A4, 0x01A6, 0x01A7,   /* 0x01A0-0x01A7 */ 
-0x01A7, 0x01A9, 0x01AA, 0x01AB, 0x01AC, 0x01AC, 0x01AE, 0x01AF,   /* 0x01A8-0x01AF */ 
-0x01AF, 0x01B1, 0x01B2, 0x01B3, 0x01B3, 0x01B5, 0x01B5, 0x01B7,   /* 0x01B0-0x01B7 */ 
-0x01B8, 0x01B8, 0x01BA, 0x01BB, 0x01BC, 0x01BC, 0x01BE, 0x01BF,   /* 0x01B8-0x01BF */ 
-0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C4, 0x01C5, 0x01C4, 0x01C7,   /* 0x01C0-0x01C7 */ 
-0x01C8, 0x01C7, 0x01CA, 0x01CB, 0x01CA, 0x01CD, 0x01CD, 0x01CF,   /* 0x01C8-0x01CF */ 
-0x01CF, 0x01D1, 0x01D1, 0x01D3, 0x01D3, 0x01D5, 0x01D5, 0x01D7,   /* 0x01D0-0x01D7 */ 
-0x01D7, 0x01D9, 0x01D9, 0x01DB, 0x01DB, 0x018E, 0x01DE, 0x01DE,   /* 0x01D8-0x01DF */ 
-0x01E0, 0x01E0, 0x01E2, 0x01E2, 0x01E4, 0x01E4, 0x01E6, 0x01E6,   /* 0x01E0-0x01E7 */ 
-0x01E8, 0x01E8, 0x01EA, 0x01EA, 0x01EC, 0x01EC, 0x01EE, 0x01EE,   /* 0x01E8-0x01EF */ 
-0x01F0, 0x01F1, 0x01F2, 0x01F1, 0x01F4, 0x01F4, 0x01F6, 0x01F7,   /* 0x01F0-0x01F7 */ 
-0x01F8, 0x01F9, 0x01FA, 0x01FA, 0x01FC, 0x01FC, 0x01FE, 0x01FE,   /* 0x01F8-0x01FF */ 
-0x0200, 0x0200, 0x0202, 0x0202, 0x0204, 0x0204, 0x0206, 0x0206,   /* 0x0200-0x0207 */ 
-0x0208, 0x0208, 0x020A, 0x020A, 0x020C, 0x020C, 0x020E, 0x020E,   /* 0x0208-0x020F */ 
-0x0210, 0x0210, 0x0212, 0x0212, 0x0214, 0x0214, 0x0216, 0x0216,   /* 0x0210-0x0217 */ 
-0x0218, 0x0219, 0x021A, 0x021B, 0x021C, 0x021D, 0x021E, 0x021F,   /* 0x0218-0x021F */ 
-0x0220, 0x0221, 0x0222, 0x0223, 0x0224, 0x0225, 0x0226, 0x0227,   /* 0x0220-0x0227 */ 
-0x0228, 0x0229, 0x022A, 0x022B, 0x022C, 0x022D, 0x022E, 0x022F,   /* 0x0228-0x022F */ 
-0x0230, 0x0231, 0x0232, 0x0233, 0x0234, 0x0235, 0x0236, 0x0237,   /* 0x0230-0x0237 */ 
-0x0238, 0x0239, 0x023A, 0x023B, 0x023C, 0x023D, 0x023E, 0x023F,   /* 0x0238-0x023F */ 
-0x0240, 0x0241, 0x0242, 0x0243, 0x0244, 0x0245, 0x0246, 0x0247,   /* 0x0240-0x0247 */ 
-0x0248, 0x0249, 0x024A, 0x024B, 0x024C, 0x024D, 0x024E, 0x024F,   /* 0x0248-0x024F */ 
-0x0250, 0x0251, 0x0252, 0x0181, 0x0186, 0x0255, 0x0189, 0x018A,   /* 0x0250-0x0257 */ 
-0x0258, 0x018F, 0x025A, 0x0190, 0x025C, 0x025D, 0x025E, 0x025F,   /* 0x0258-0x025F */ 
-0x0193, 0x0261, 0x0262, 0x0194, 0x0264, 0x0265, 0x0266, 0x0267,   /* 0x0260-0x0267 */ 
-0x0197, 0x0196, 0x026A, 0x026B, 0x026C, 0x026D, 0x026E, 0x019C,   /* 0x0268-0x026F */ 
-0x0270, 0x0271, 0x019D, 0x0273, 0x0274, 0x019F, 0x0276, 0x0277,   /* 0x0270-0x0277 */ 
-0x0278, 0x0279, 0x027A, 0x027B, 0x027C, 0x027D, 0x027E, 0x027F,   /* 0x0278-0x027F */ 
-0x0280, 0x0281, 0x0282, 0x01A9, 0x0284, 0x0285, 0x0286, 0x0287,   /* 0x0280-0x0287 */ 
-0x01AE, 0x0289, 0x01B1, 0x01B2, 0x028C, 0x028D, 0x028E, 0x028F,   /* 0x0288-0x028F */ 
-0x0290, 0x0291, 0x01B7, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297,   /* 0x0290-0x0297 */ 
-0x0298, 0x0299, 0x029A, 0x029B, 0x029C, 0x029D, 0x029E, 0x029F,   /* 0x0298-0x029F */ 
-0x02A0, 0x02A1, 0x02A2, 0x02A3, 0x02A4, 0x02A5, 0x02A6, 0x02A7,   /* 0x02A0-0x02A7 */ 
-0x02A8, 0x02A9, 0x02AA, 0x02AB, 0x02AC, 0x02AD, 0x02AE, 0x02AF,   /* 0x02A8-0x02AF */ 
-0x02B0, 0x02B1, 0x02B2, 0x02B3, 0x02B4, 0x02B5, 0x02B6, 0x02B7,   /* 0x02B0-0x02B7 */ 
-0x02B8, 0x02B9, 0x02BA, 0x02BB, 0x02BC, 0x02BD, 0x02BE, 0x02BF }; /* 0x02B8-0x02BF */
-
-static const u_int16_t upcase_table_3[384] = {
-0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0386, 0x0387,   /* 0x0380-0x0387 */ 
-0x0388, 0x0389, 0x038A, 0x038B, 0x038C, 0x038D, 0x038E, 0x038F,   /* 0x0388-0x038F */ 
-0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,   /* 0x0390-0x0397 */ 
-0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,   /* 0x0398-0x039F */ 
-0x03A0, 0x03A1, 0x03A2, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7,   /* 0x03A0-0x03A7 */ 
-0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x0386, 0x0388, 0x0389, 0x038A,   /* 0x03A8-0x03AF */ 
-0x03B0, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,   /* 0x03B0-0x03B7 */ 
-0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,   /* 0x03B8-0x03BF */ 
-0x03A0, 0x03A1, 0x03A3, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7,   /* 0x03C0-0x03C7 */ 
-0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x038C, 0x038E, 0x038F, 0x03CF,   /* 0x03C8-0x03CF */ 
-0x03D0, 0x03D1, 0x03D2, 0x03D3, 0x03D4, 0x03D5, 0x03D6, 0x03D7,   /* 0x03D0-0x03D7 */ 
-0x03D8, 0x03D9, 0x03DA, 0x03DB, 0x03DC, 0x03DD, 0x03DE, 0x03DF,   /* 0x03D8-0x03DF */ 
-0x03E0, 0x03E1, 0x03E2, 0x03E2, 0x03E4, 0x03E4, 0x03E6, 0x03E6,   /* 0x03E0-0x03E7 */ 
-0x03E8, 0x03E8, 0x03EA, 0x03EA, 0x03EC, 0x03EC, 0x03EE, 0x03EE,   /* 0x03E8-0x03EF */ 
-0x03F0, 0x03F1, 0x03F2, 0x03F3, 0x03F4, 0x03F5, 0x03F6, 0x03F7,   /* 0x03F0-0x03F7 */ 
-0x03F8, 0x03F9, 0x03FA, 0x03FB, 0x03FC, 0x03FD, 0x03FE, 0x03FF,   /* 0x03F8-0x03FF */ 
-0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407,   /* 0x0400-0x0407 */ 
-0x0408, 0x0409, 0x040A, 0x040B, 0x040C, 0x040D, 0x040E, 0x040F,   /* 0x0408-0x040F */ 
-0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,   /* 0x0410-0x0417 */ 
-0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,   /* 0x0418-0x041F */ 
-0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,   /* 0x0420-0x0427 */ 
-0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,   /* 0x0428-0x042F */ 
-0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,   /* 0x0430-0x0437 */ 
-0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,   /* 0x0438-0x043F */ 
-0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,   /* 0x0440-0x0447 */ 
-0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,   /* 0x0448-0x044F */ 
-0x0450, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407,   /* 0x0450-0x0457 */ 
-0x0408, 0x0409, 0x040A, 0x040B, 0x040C, 0x045D, 0x040E, 0x040F,   /* 0x0458-0x045F */ 
-0x0460, 0x0460, 0x0462, 0x0462, 0x0464, 0x0464, 0x0466, 0x0466,   /* 0x0460-0x0467 */ 
-0x0468, 0x0468, 0x046A, 0x046A, 0x046C, 0x046C, 0x046E, 0x046E,   /* 0x0468-0x046F */ 
-0x0470, 0x0470, 0x0472, 0x0472, 0x0474, 0x0474, 0x0476, 0x0476,   /* 0x0470-0x0477 */ 
-0x0478, 0x0478, 0x047A, 0x047A, 0x047C, 0x047C, 0x047E, 0x047E,   /* 0x0478-0x047F */ 
-0x0480, 0x0480, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487,   /* 0x0480-0x0487 */ 
-0x0488, 0x0489, 0x048A, 0x048B, 0x048C, 0x048D, 0x048E, 0x048F,   /* 0x0488-0x048F */ 
-0x0490, 0x0490, 0x0492, 0x0492, 0x0494, 0x0494, 0x0496, 0x0496,   /* 0x0490-0x0497 */ 
-0x0498, 0x0498, 0x049A, 0x049A, 0x049C, 0x049C, 0x049E, 0x049E,   /* 0x0498-0x049F */ 
-0x04A0, 0x04A0, 0x04A2, 0x04A2, 0x04A4, 0x04A4, 0x04A6, 0x04A6,   /* 0x04A0-0x04A7 */ 
-0x04A8, 0x04A8, 0x04AA, 0x04AA, 0x04AC, 0x04AC, 0x04AE, 0x04AE,   /* 0x04A8-0x04AF */ 
-0x04B0, 0x04B0, 0x04B2, 0x04B2, 0x04B4, 0x04B4, 0x04B6, 0x04B6,   /* 0x04B0-0x04B7 */ 
-0x04B8, 0x04B8, 0x04BA, 0x04BA, 0x04BC, 0x04BC, 0x04BE, 0x04BE,   /* 0x04B8-0x04BF */ 
-0x04C0, 0x04C1, 0x04C1, 0x04C3, 0x04C3, 0x04C5, 0x04C6, 0x04C7,   /* 0x04C0-0x04C7 */ 
-0x04C7, 0x04C9, 0x04CA, 0x04CB, 0x04CB, 0x04CD, 0x04CE, 0x04CF,   /* 0x04C8-0x04CF */ 
-0x04D0, 0x04D0, 0x04D2, 0x04D2, 0x04D4, 0x04D4, 0x04D6, 0x04D6,   /* 0x04D0-0x04D7 */ 
-0x04D8, 0x04D8, 0x04DA, 0x04DA, 0x04DC, 0x04DC, 0x04DE, 0x04DE,   /* 0x04D8-0x04DF */ 
-0x04E0, 0x04E0, 0x04E2, 0x04E2, 0x04E4, 0x04E4, 0x04E6, 0x04E6,   /* 0x04E0-0x04E7 */ 
-0x04E8, 0x04E8, 0x04EA, 0x04EA, 0x04EC, 0x04ED, 0x04EE, 0x04EE,   /* 0x04E8-0x04EF */ 
-0x04F0, 0x04F0, 0x04F2, 0x04F2, 0x04F4, 0x04F4, 0x04F6, 0x04F7,   /* 0x04F0-0x04F7 */ 
-0x04F8, 0x04F8, 0x04FA, 0x04FB, 0x04FC, 0x04FD, 0x04FE, 0x04FF }; /* 0x04F8-0x04FF */
-
-static const u_int16_t upcase_table_4[128] = {
-0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547,   /* 0x0540-0x0547 */ 
-0x0548, 0x0549, 0x054A, 0x054B, 0x054C, 0x054D, 0x054E, 0x054F,   /* 0x0548-0x054F */ 
-0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556, 0x0557,   /* 0x0550-0x0557 */ 
-0x0558, 0x0559, 0x055A, 0x055B, 0x055C, 0x055D, 0x055E, 0x055F,   /* 0x0558-0x055F */ 
-0x0560, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537,   /* 0x0560-0x0567 */ 
-0x0538, 0x0539, 0x053A, 0x053B, 0x053C, 0x053D, 0x053E, 0x053F,   /* 0x0568-0x056F */ 
-0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547,   /* 0x0570-0x0577 */ 
-0x0548, 0x0549, 0x054A, 0x054B, 0x054C, 0x054D, 0x054E, 0x054F,   /* 0x0578-0x057F */ 
-0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556, 0x0587,   /* 0x0580-0x0587 */ 
-0x0588, 0x0589, 0x058A, 0x058B, 0x058C, 0x058D, 0x058E, 0x058F,   /* 0x0588-0x058F */ 
-0x0590, 0x0591, 0x0592, 0x0593, 0x0594, 0x0595, 0x0596, 0x0597,   /* 0x0590-0x0597 */ 
-0x0598, 0x0599, 0x059A, 0x059B, 0x059C, 0x059D, 0x059E, 0x059F,   /* 0x0598-0x059F */ 
-0x05A0, 0x05A1, 0x05A2, 0x05A3, 0x05A4, 0x05A5, 0x05A6, 0x05A7,   /* 0x05A0-0x05A7 */ 
-0x05A8, 0x05A9, 0x05AA, 0x05AB, 0x05AC, 0x05AD, 0x05AE, 0x05AF,   /* 0x05A8-0x05AF */ 
-0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7,   /* 0x05B0-0x05B7 */ 
-0x05B8, 0x05B9, 0x05BA, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF }; /* 0x05B8-0x05BF */
-
-static const u_int16_t upcase_table_5[512] = {
-0x1E00, 0x1E00, 0x1E02, 0x1E02, 0x1E04, 0x1E04, 0x1E06, 0x1E06,   /* 0x1E00-0x1E07 */ 
-0x1E08, 0x1E08, 0x1E0A, 0x1E0A, 0x1E0C, 0x1E0C, 0x1E0E, 0x1E0E,   /* 0x1E08-0x1E0F */ 
-0x1E10, 0x1E10, 0x1E12, 0x1E12, 0x1E14, 0x1E14, 0x1E16, 0x1E16,   /* 0x1E10-0x1E17 */ 
-0x1E18, 0x1E18, 0x1E1A, 0x1E1A, 0x1E1C, 0x1E1C, 0x1E1E, 0x1E1E,   /* 0x1E18-0x1E1F */ 
-0x1E20, 0x1E20, 0x1E22, 0x1E22, 0x1E24, 0x1E24, 0x1E26, 0x1E26,   /* 0x1E20-0x1E27 */ 
-0x1E28, 0x1E28, 0x1E2A, 0x1E2A, 0x1E2C, 0x1E2C, 0x1E2E, 0x1E2E,   /* 0x1E28-0x1E2F */ 
-0x1E30, 0x1E30, 0x1E32, 0x1E32, 0x1E34, 0x1E34, 0x1E36, 0x1E36,   /* 0x1E30-0x1E37 */ 
-0x1E38, 0x1E38, 0x1E3A, 0x1E3A, 0x1E3C, 0x1E3C, 0x1E3E, 0x1E3E,   /* 0x1E38-0x1E3F */ 
-0x1E40, 0x1E40, 0x1E42, 0x1E42, 0x1E44, 0x1E44, 0x1E46, 0x1E46,   /* 0x1E40-0x1E47 */ 
-0x1E48, 0x1E48, 0x1E4A, 0x1E4A, 0x1E4C, 0x1E4C, 0x1E4E, 0x1E4E,   /* 0x1E48-0x1E4F */ 
-0x1E50, 0x1E50, 0x1E52, 0x1E52, 0x1E54, 0x1E54, 0x1E56, 0x1E56,   /* 0x1E50-0x1E57 */ 
-0x1E58, 0x1E58, 0x1E5A, 0x1E5A, 0x1E5C, 0x1E5C, 0x1E5E, 0x1E5E,   /* 0x1E58-0x1E5F */ 
-0x1E60, 0x1E60, 0x1E62, 0x1E62, 0x1E64, 0x1E64, 0x1E66, 0x1E66,   /* 0x1E60-0x1E67 */ 
-0x1E68, 0x1E68, 0x1E6A, 0x1E6A, 0x1E6C, 0x1E6C, 0x1E6E, 0x1E6E,   /* 0x1E68-0x1E6F */ 
-0x1E70, 0x1E70, 0x1E72, 0x1E72, 0x1E74, 0x1E74, 0x1E76, 0x1E76,   /* 0x1E70-0x1E77 */ 
-0x1E78, 0x1E78, 0x1E7A, 0x1E7A, 0x1E7C, 0x1E7C, 0x1E7E, 0x1E7E,   /* 0x1E78-0x1E7F */ 
-0x1E80, 0x1E80, 0x1E82, 0x1E82, 0x1E84, 0x1E84, 0x1E86, 0x1E86,   /* 0x1E80-0x1E87 */ 
-0x1E88, 0x1E88, 0x1E8A, 0x1E8A, 0x1E8C, 0x1E8C, 0x1E8E, 0x1E8E,   /* 0x1E88-0x1E8F */ 
-0x1E90, 0x1E90, 0x1E92, 0x1E92, 0x1E94, 0x1E94, 0x1E96, 0x1E97,   /* 0x1E90-0x1E97 */ 
-0x1E98, 0x1E99, 0x1E9A, 0x1E9B, 0x1E9C, 0x1E9D, 0x1E9E, 0x1E9F,   /* 0x1E98-0x1E9F */ 
-0x1EA0, 0x1EA0, 0x1EA2, 0x1EA2, 0x1EA4, 0x1EA4, 0x1EA6, 0x1EA6,   /* 0x1EA0-0x1EA7 */ 
-0x1EA8, 0x1EA8, 0x1EAA, 0x1EAA, 0x1EAC, 0x1EAC, 0x1EAE, 0x1EAE,   /* 0x1EA8-0x1EAF */ 
-0x1EB0, 0x1EB0, 0x1EB2, 0x1EB2, 0x1EB4, 0x1EB4, 0x1EB6, 0x1EB6,   /* 0x1EB0-0x1EB7 */ 
-0x1EB8, 0x1EB8, 0x1EBA, 0x1EBA, 0x1EBC, 0x1EBC, 0x1EBE, 0x1EBE,   /* 0x1EB8-0x1EBF */ 
-0x1EC0, 0x1EC0, 0x1EC2, 0x1EC2, 0x1EC4, 0x1EC4, 0x1EC6, 0x1EC6,   /* 0x1EC0-0x1EC7 */ 
-0x1EC8, 0x1EC8, 0x1ECA, 0x1ECA, 0x1ECC, 0x1ECC, 0x1ECE, 0x1ECE,   /* 0x1EC8-0x1ECF */ 
-0x1ED0, 0x1ED0, 0x1ED2, 0x1ED2, 0x1ED4, 0x1ED4, 0x1ED6, 0x1ED6,   /* 0x1ED0-0x1ED7 */ 
-0x1ED8, 0x1ED8, 0x1EDA, 0x1EDA, 0x1EDC, 0x1EDC, 0x1EDE, 0x1EDE,   /* 0x1ED8-0x1EDF */ 
-0x1EE0, 0x1EE0, 0x1EE2, 0x1EE2, 0x1EE4, 0x1EE4, 0x1EE6, 0x1EE6,   /* 0x1EE0-0x1EE7 */ 
-0x1EE8, 0x1EE8, 0x1EEA, 0x1EEA, 0x1EEC, 0x1EEC, 0x1EEE, 0x1EEE,   /* 0x1EE8-0x1EEF */ 
-0x1EF0, 0x1EF0, 0x1EF2, 0x1EF2, 0x1EF4, 0x1EF4, 0x1EF6, 0x1EF6,   /* 0x1EF0-0x1EF7 */ 
-0x1EF8, 0x1EF8, 0x1EFA, 0x1EFB, 0x1EFC, 0x1EFD, 0x1EFE, 0x1EFF,   /* 0x1EF8-0x1EFF */ 
-0x1F08, 0x1F09, 0x1F0A, 0x1F0B, 0x1F0C, 0x1F0D, 0x1F0E, 0x1F0F,   /* 0x1F00-0x1F07 */ 
-0x1F08, 0x1F09, 0x1F0A, 0x1F0B, 0x1F0C, 0x1F0D, 0x1F0E, 0x1F0F,   /* 0x1F08-0x1F0F */ 
-0x1F18, 0x1F19, 0x1F1A, 0x1F1B, 0x1F1C, 0x1F1D, 0x1F16, 0x1F17,   /* 0x1F10-0x1F17 */ 
-0x1F18, 0x1F19, 0x1F1A, 0x1F1B, 0x1F1C, 0x1F1D, 0x1F1E, 0x1F1F,   /* 0x1F18-0x1F1F */ 
-0x1F28, 0x1F29, 0x1F2A, 0x1F2B, 0x1F2C, 0x1F2D, 0x1F2E, 0x1F2F,   /* 0x1F20-0x1F27 */ 
-0x1F28, 0x1F29, 0x1F2A, 0x1F2B, 0x1F2C, 0x1F2D, 0x1F2E, 0x1F2F,   /* 0x1F28-0x1F2F */ 
-0x1F38, 0x1F39, 0x1F3A, 0x1F3B, 0x1F3C, 0x1F3D, 0x1F3E, 0x1F3F,   /* 0x1F30-0x1F37 */ 
-0x1F38, 0x1F39, 0x1F3A, 0x1F3B, 0x1F3C, 0x1F3D, 0x1F3E, 0x1F3F,   /* 0x1F38-0x1F3F */ 
-0x1F48, 0x1F49, 0x1F4A, 0x1F4B, 0x1F4C, 0x1F4D, 0x1F46, 0x1F47,   /* 0x1F40-0x1F47 */ 
-0x1F48, 0x1F49, 0x1F4A, 0x1F4B, 0x1F4C, 0x1F4D, 0x1F4E, 0x1F4F,   /* 0x1F48-0x1F4F */ 
-0x1F50, 0x1F59, 0x1F52, 0x1F5B, 0x1F54, 0x1F5D, 0x1F56, 0x1F5F,   /* 0x1F50-0x1F57 */ 
-0x1F58, 0x1F59, 0x1F5A, 0x1F5B, 0x1F5C, 0x1F5D, 0x1F5E, 0x1F5F,   /* 0x1F58-0x1F5F */ 
-0x1F68, 0x1F69, 0x1F6A, 0x1F6B, 0x1F6C, 0x1F6D, 0x1F6E, 0x1F6F,   /* 0x1F60-0x1F67 */ 
-0x1F68, 0x1F69, 0x1F6A, 0x1F6B, 0x1F6C, 0x1F6D, 0x1F6E, 0x1F6F,   /* 0x1F68-0x1F6F */ 
-0x1FBA, 0x1FBB, 0x1FC8, 0x1FC9, 0x1FCA, 0x1FCB, 0x1FDA, 0x1FDB,   /* 0x1F70-0x1F77 */ 
-0x1FF8, 0x1FF9, 0x1FEA, 0x1FEB, 0x1FFA, 0x1FFB, 0x1F7E, 0x1F7F,   /* 0x1F78-0x1F7F */ 
-0x1F80, 0x1F81, 0x1F82, 0x1F83, 0x1F84, 0x1F85, 0x1F86, 0x1F87,   /* 0x1F80-0x1F87 */ 
-0x1F88, 0x1F89, 0x1F8A, 0x1F8B, 0x1F8C, 0x1F8D, 0x1F8E, 0x1F8F,   /* 0x1F88-0x1F8F */ 
-0x1F90, 0x1F91, 0x1F92, 0x1F93, 0x1F94, 0x1F95, 0x1F96, 0x1F97,   /* 0x1F90-0x1F97 */ 
-0x1F98, 0x1F99, 0x1F9A, 0x1F9B, 0x1F9C, 0x1F9D, 0x1F9E, 0x1F9F,   /* 0x1F98-0x1F9F */ 
-0x1FA0, 0x1FA1, 0x1FA2, 0x1FA3, 0x1FA4, 0x1FA5, 0x1FA6, 0x1FA7,   /* 0x1FA0-0x1FA7 */ 
-0x1FA8, 0x1FA9, 0x1FAA, 0x1FAB, 0x1FAC, 0x1FAD, 0x1FAE, 0x1FAF,   /* 0x1FA8-0x1FAF */ 
-0x1FB8, 0x1FB9, 0x1FB2, 0x1FB3, 0x1FB4, 0x1FB5, 0x1FB6, 0x1FB7,   /* 0x1FB0-0x1FB7 */ 
-0x1FB8, 0x1FB9, 0x1FBA, 0x1FBB, 0x1FBC, 0x1FBD, 0x1FBE, 0x1FBF,   /* 0x1FB8-0x1FBF */ 
-0x1FC0, 0x1FC1, 0x1FC2, 0x1FC3, 0x1FC4, 0x1FC5, 0x1FC6, 0x1FC7,   /* 0x1FC0-0x1FC7 */ 
-0x1FC8, 0x1FC9, 0x1FCA, 0x1FCB, 0x1FCC, 0x1FCD, 0x1FCE, 0x1FCF,   /* 0x1FC8-0x1FCF */ 
-0x1FD8, 0x1FD9, 0x1FD2, 0x1FD3, 0x1FD4, 0x1FD5, 0x1FD6, 0x1FD7,   /* 0x1FD0-0x1FD7 */ 
-0x1FD8, 0x1FD9, 0x1FDA, 0x1FDB, 0x1FDC, 0x1FDD, 0x1FDE, 0x1FDF,   /* 0x1FD8-0x1FDF */ 
-0x1FE8, 0x1FE9, 0x1FE2, 0x1FE3, 0x1FE4, 0x1FEC, 0x1FE6, 0x1FE7,   /* 0x1FE0-0x1FE7 */ 
-0x1FE8, 0x1FE9, 0x1FEA, 0x1FEB, 0x1FEC, 0x1FED, 0x1FEE, 0x1FEF,   /* 0x1FE8-0x1FEF */ 
-0x1FF0, 0x1FF1, 0x1FF2, 0x1FF3, 0x1FF4, 0x1FF5, 0x1FF6, 0x1FF7,   /* 0x1FF0-0x1FF7 */ 
-0x1FF8, 0x1FF9, 0x1FFA, 0x1FFB, 0x1FFC, 0x1FFD, 0x1FFE, 0x1FFF }; /* 0x1FF8-0x1FFF */
-
-static const u_int16_t upcase_table_6[64] = {
-0x2140, 0x2141, 0x2142, 0x2143, 0x2144, 0x2145, 0x2146, 0x2147,   /* 0x2140-0x2147 */ 
-0x2148, 0x2149, 0x214A, 0x214B, 0x214C, 0x214D, 0x214E, 0x214F,   /* 0x2148-0x214F */ 
-0x2150, 0x2151, 0x2152, 0x2153, 0x2154, 0x2155, 0x2156, 0x2157,   /* 0x2150-0x2157 */ 
-0x2158, 0x2159, 0x215A, 0x215B, 0x215C, 0x215D, 0x215E, 0x215F,   /* 0x2158-0x215F */ 
-0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167,   /* 0x2160-0x2167 */ 
-0x2168, 0x2169, 0x216A, 0x216B, 0x216C, 0x216D, 0x216E, 0x216F,   /* 0x2168-0x216F */ 
-0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167,   /* 0x2170-0x2177 */ 
-0x2168, 0x2169, 0x216A, 0x216B, 0x216C, 0x216D, 0x216E, 0x216F }; /* 0x2178-0x217F */
-
-static const u_int16_t upcase_table_7[64] = {
-0x24C0, 0x24C1, 0x24C2, 0x24C3, 0x24C4, 0x24C5, 0x24C6, 0x24C7,   /* 0x24C0-0x24C7 */ 
-0x24C8, 0x24C9, 0x24CA, 0x24CB, 0x24CC, 0x24CD, 0x24CE, 0x24CF,   /* 0x24C8-0x24CF */ 
-0x24B6, 0x24B7, 0x24B8, 0x24B9, 0x24BA, 0x24BB, 0x24BC, 0x24BD,   /* 0x24D0-0x24D7 */ 
-0x24BE, 0x24BF, 0x24C0, 0x24C1, 0x24C2, 0x24C3, 0x24C4, 0x24C5,   /* 0x24D8-0x24DF */ 
-0x24C6, 0x24C7, 0x24C8, 0x24C9, 0x24CA, 0x24CB, 0x24CC, 0x24CD,   /* 0x24E0-0x24E7 */ 
-0x24CE, 0x24CF, 0x24EA, 0x24EB, 0x24EC, 0x24ED, 0x24EE, 0x24EF,   /* 0x24E8-0x24EF */ 
-0x24F0, 0x24F1, 0x24F2, 0x24F3, 0x24F4, 0x24F5, 0x24F6, 0x24F7,   /* 0x24F0-0x24F7 */ 
-0x24F8, 0x24F9, 0x24FA, 0x24FB, 0x24FC, 0x24FD, 0x24FE, 0x24FF }; /* 0x24F8-0x24FF */
-
-static const u_int16_t upcase_table_8[64] = {
-0xFF40, 0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27,   /* 0xFF40-0xFF47 */ 
-0xFF28, 0xFF29, 0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F,   /* 0xFF48-0xFF4F */ 
-0xFF30, 0xFF31, 0xFF32, 0xFF33, 0xFF34, 0xFF35, 0xFF36, 0xFF37,   /* 0xFF50-0xFF57 */ 
-0xFF38, 0xFF39, 0xFF3A, 0xFF5B, 0xFF5C, 0xFF5D, 0xFF5E, 0xFF5F,   /* 0xFF58-0xFF5F */ 
-0xFF60, 0xFF61, 0xFF62, 0xFF63, 0xFF64, 0xFF65, 0xFF66, 0xFF67,   /* 0xFF60-0xFF67 */ 
-0xFF68, 0xFF69, 0xFF6A, 0xFF6B, 0xFF6C, 0xFF6D, 0xFF6E, 0xFF6F,   /* 0xFF68-0xFF6F */ 
-0xFF70, 0xFF71, 0xFF72, 0xFF73, 0xFF74, 0xFF75, 0xFF76, 0xFF77,   /* 0xFF70-0xFF77 */ 
-0xFF78, 0xFF79, 0xFF7A, 0xFF7B, 0xFF7C, 0xFF7D, 0xFF7E, 0xFF7F }; /* 0xFF78-0xFF7F */
-
-
-
-static const u_int16_t lowcase_table_1[64] = {
-0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,   /* 0x0040-0x0047 */ 
-0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,   /* 0x0048-0x004F */ 
-0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,   /* 0x0050-0x0057 */ 
-0x0078, 0x0079, 0x007A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,   /* 0x0058-0x005F */ 
-0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,   /* 0x0060-0x0067 */ 
-0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,   /* 0x0068-0x006F */ 
-0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,   /* 0x0070-0x0077 */ 
-0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F }; /* 0x0078-0x007F */
-
-static const u_int16_t lowcase_table_2[384] = {
-0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,   /* 0x00C0-0x00C7 */ 
-0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,   /* 0x00C8-0x00CF */ 
-0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00D7,   /* 0x00D0-0x00D7 */ 
-0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00DF,   /* 0x00D8-0x00DF */ 
-0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,   /* 0x00E0-0x00E7 */ 
-0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,   /* 0x00E8-0x00EF */ 
-0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,   /* 0x00F0-0x00F7 */ 
-0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF,   /* 0x00F8-0x00FF */ 
-0x0101, 0x0101, 0x0103, 0x0103, 0x0105, 0x0105, 0x0107, 0x0107,   /* 0x0100-0x0107 */ 
-0x0109, 0x0109, 0x010B, 0x010B, 0x010D, 0x010D, 0x010F, 0x010F,   /* 0x0108-0x010F */ 
-0x0111, 0x0111, 0x0113, 0x0113, 0x0115, 0x0115, 0x0117, 0x0117,   /* 0x0110-0x0117 */ 
-0x0119, 0x0119, 0x011B, 0x011B, 0x011D, 0x011D, 0x011F, 0x011F,   /* 0x0118-0x011F */ 
-0x0121, 0x0121, 0x0123, 0x0123, 0x0125, 0x0125, 0x0127, 0x0127,   /* 0x0120-0x0127 */ 
-0x0129, 0x0129, 0x012B, 0x012B, 0x012D, 0x012D, 0x012F, 0x012F,   /* 0x0128-0x012F */ 
-0x0130, 0x0131, 0x0133, 0x0133, 0x0135, 0x0135, 0x0137, 0x0137,   /* 0x0130-0x0137 */ 
-0x0138, 0x013A, 0x013A, 0x013C, 0x013C, 0x013E, 0x013E, 0x0140,   /* 0x0138-0x013F */ 
-0x0140, 0x0142, 0x0142, 0x0144, 0x0144, 0x0146, 0x0146, 0x0148,   /* 0x0140-0x0147 */ 
-0x0148, 0x0149, 0x014B, 0x014B, 0x014D, 0x014D, 0x014F, 0x014F,   /* 0x0148-0x014F */ 
-0x0151, 0x0151, 0x0153, 0x0153, 0x0155, 0x0155, 0x0157, 0x0157,   /* 0x0150-0x0157 */ 
-0x0159, 0x0159, 0x015B, 0x015B, 0x015D, 0x015D, 0x015F, 0x015F,   /* 0x0158-0x015F */ 
-0x0161, 0x0161, 0x0163, 0x0163, 0x0165, 0x0165, 0x0167, 0x0167,   /* 0x0160-0x0167 */ 
-0x0169, 0x0169, 0x016B, 0x016B, 0x016D, 0x016D, 0x016F, 0x016F,   /* 0x0168-0x016F */ 
-0x0171, 0x0171, 0x0173, 0x0173, 0x0175, 0x0175, 0x0177, 0x0177,   /* 0x0170-0x0177 */ 
-0x00FF, 0x017A, 0x017A, 0x017C, 0x017C, 0x017E, 0x017E, 0x017F,   /* 0x0178-0x017F */ 
-0x0180, 0x0253, 0x0183, 0x0183, 0x0185, 0x0185, 0x0254, 0x0188,   /* 0x0180-0x0187 */ 
-0x0188, 0x0256, 0x0257, 0x018C, 0x018C, 0x018D, 0x01DD, 0x0259,   /* 0x0188-0x018F */ 
-0x025B, 0x0192, 0x0192, 0x0260, 0x0263, 0x0195, 0x0269, 0x0268,   /* 0x0190-0x0197 */ 
-0x0199, 0x0199, 0x019A, 0x019B, 0x026F, 0x0272, 0x019E, 0x0275,   /* 0x0198-0x019F */ 
-0x01A1, 0x01A1, 0x01A3, 0x01A3, 0x01A5, 0x01A5, 0x01A6, 0x01A8,   /* 0x01A0-0x01A7 */ 
-0x01A8, 0x0283, 0x01AA, 0x01AB, 0x01AD, 0x01AD, 0x0288, 0x01B0,   /* 0x01A8-0x01AF */ 
-0x01B0, 0x028A, 0x028B, 0x01B4, 0x01B4, 0x01B6, 0x01B6, 0x0292,   /* 0x01B0-0x01B7 */ 
-0x01B9, 0x01B9, 0x01BA, 0x01BB, 0x01BD, 0x01BD, 0x01BE, 0x01BF,   /* 0x01B8-0x01BF */ 
-0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C6, 0x01C5, 0x01C6, 0x01C9,   /* 0x01C0-0x01C7 */ 
-0x01C8, 0x01C9, 0x01CC, 0x01CB, 0x01CC, 0x01CE, 0x01CE, 0x01D0,   /* 0x01C8-0x01CF */ 
-0x01D0, 0x01D2, 0x01D2, 0x01D4, 0x01D4, 0x01D6, 0x01D6, 0x01D8,   /* 0x01D0-0x01D7 */ 
-0x01D8, 0x01DA, 0x01DA, 0x01DC, 0x01DC, 0x01DD, 0x01DF, 0x01DF,   /* 0x01D8-0x01DF */ 
-0x01E1, 0x01E1, 0x01E3, 0x01E3, 0x01E5, 0x01E5, 0x01E7, 0x01E7,   /* 0x01E0-0x01E7 */ 
-0x01E9, 0x01E9, 0x01EB, 0x01EB, 0x01ED, 0x01ED, 0x01EF, 0x01EF,   /* 0x01E8-0x01EF */ 
-0x01F0, 0x01F3, 0x01F2, 0x01F3, 0x01F5, 0x01F5, 0x01F6, 0x01F7,   /* 0x01F0-0x01F7 */ 
-0x01F8, 0x01F9, 0x01FB, 0x01FB, 0x01FD, 0x01FD, 0x01FF, 0x01FF,   /* 0x01F8-0x01FF */ 
-0x0201, 0x0201, 0x0203, 0x0203, 0x0205, 0x0205, 0x0207, 0x0207,   /* 0x0200-0x0207 */ 
-0x0209, 0x0209, 0x020B, 0x020B, 0x020D, 0x020D, 0x020F, 0x020F,   /* 0x0208-0x020F */ 
-0x0211, 0x0211, 0x0213, 0x0213, 0x0215, 0x0215, 0x0217, 0x0217,   /* 0x0210-0x0217 */ 
-0x0218, 0x0219, 0x021A, 0x021B, 0x021C, 0x021D, 0x021E, 0x021F,   /* 0x0218-0x021F */ 
-0x0220, 0x0221, 0x0222, 0x0223, 0x0224, 0x0225, 0x0226, 0x0227,   /* 0x0220-0x0227 */ 
-0x0228, 0x0229, 0x022A, 0x022B, 0x022C, 0x022D, 0x022E, 0x022F,   /* 0x0228-0x022F */ 
-0x0230, 0x0231, 0x0232, 0x0233, 0x0234, 0x0235, 0x0236, 0x0237,   /* 0x0230-0x0237 */ 
-0x0238, 0x0239, 0x023A, 0x023B, 0x023C, 0x023D, 0x023E, 0x023F }; /* 0x0238-0x023F */
-
-static const u_int16_t lowcase_table_3[512] = {
-0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x03AC, 0x0387,   /* 0x0380-0x0387 */ 
-0x03AD, 0x03AE, 0x03AF, 0x038B, 0x03CC, 0x038D, 0x03CD, 0x03CE,   /* 0x0388-0x038F */ 
-0x0390, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,   /* 0x0390-0x0397 */ 
-0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,   /* 0x0398-0x039F */ 
-0x03C0, 0x03C1, 0x03A2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,   /* 0x03A0-0x03A7 */ 
-0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03AC, 0x03AD, 0x03AE, 0x03AF,   /* 0x03A8-0x03AF */ 
-0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,   /* 0x03B0-0x03B7 */ 
-0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,   /* 0x03B8-0x03BF */ 
-0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,   /* 0x03C0-0x03C7 */ 
-0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x03CF,   /* 0x03C8-0x03CF */ 
-0x03D0, 0x03D1, 0x03D2, 0x03D3, 0x03D4, 0x03D5, 0x03D6, 0x03D7,   /* 0x03D0-0x03D7 */ 
-0x03D8, 0x03D9, 0x03DA, 0x03DB, 0x03DC, 0x03DD, 0x03DE, 0x03DF,   /* 0x03D8-0x03DF */ 
-0x03E0, 0x03E1, 0x03E3, 0x03E3, 0x03E5, 0x03E5, 0x03E7, 0x03E7,   /* 0x03E0-0x03E7 */ 
-0x03E9, 0x03E9, 0x03EB, 0x03EB, 0x03ED, 0x03ED, 0x03EF, 0x03EF,   /* 0x03E8-0x03EF */ 
-0x03F0, 0x03F1, 0x03F2, 0x03F3, 0x03F4, 0x03F5, 0x03F6, 0x03F7,   /* 0x03F0-0x03F7 */ 
-0x03F8, 0x03F9, 0x03FA, 0x03FB, 0x03FC, 0x03FD, 0x03FE, 0x03FF,   /* 0x03F8-0x03FF */ 
-0x0400, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457,   /* 0x0400-0x0407 */ 
-0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x040D, 0x045E, 0x045F,   /* 0x0408-0x040F */ 
-0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,   /* 0x0410-0x0417 */ 
-0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,   /* 0x0418-0x041F */ 
-0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,   /* 0x0420-0x0427 */ 
-0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,   /* 0x0428-0x042F */ 
-0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,   /* 0x0430-0x0437 */ 
-0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,   /* 0x0438-0x043F */ 
-0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,   /* 0x0440-0x0447 */ 
-0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,   /* 0x0448-0x044F */ 
-0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457,   /* 0x0450-0x0457 */ 
-0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x045D, 0x045E, 0x045F,   /* 0x0458-0x045F */ 
-0x0461, 0x0461, 0x0463, 0x0463, 0x0465, 0x0465, 0x0467, 0x0467,   /* 0x0460-0x0467 */ 
-0x0469, 0x0469, 0x046B, 0x046B, 0x046D, 0x046D, 0x046F, 0x046F,   /* 0x0468-0x046F */ 
-0x0471, 0x0471, 0x0473, 0x0473, 0x0475, 0x0475, 0x0477, 0x0477,   /* 0x0470-0x0477 */ 
-0x0479, 0x0479, 0x047B, 0x047B, 0x047D, 0x047D, 0x047F, 0x047F,   /* 0x0478-0x047F */ 
-0x0481, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487,   /* 0x0480-0x0487 */ 
-0x0488, 0x0489, 0x048A, 0x048B, 0x048C, 0x048D, 0x048E, 0x048F,   /* 0x0488-0x048F */ 
-0x0491, 0x0491, 0x0493, 0x0493, 0x0495, 0x0495, 0x0497, 0x0497,   /* 0x0490-0x0497 */ 
-0x0499, 0x0499, 0x049B, 0x049B, 0x049D, 0x049D, 0x049F, 0x049F,   /* 0x0498-0x049F */ 
-0x04A1, 0x04A1, 0x04A3, 0x04A3, 0x04A5, 0x04A5, 0x04A7, 0x04A7,   /* 0x04A0-0x04A7 */ 
-0x04A9, 0x04A9, 0x04AB, 0x04AB, 0x04AD, 0x04AD, 0x04AF, 0x04AF,   /* 0x04A8-0x04AF */ 
-0x04B1, 0x04B1, 0x04B3, 0x04B3, 0x04B5, 0x04B5, 0x04B7, 0x04B7,   /* 0x04B0-0x04B7 */ 
-0x04B9, 0x04B9, 0x04BB, 0x04BB, 0x04BD, 0x04BD, 0x04BF, 0x04BF,   /* 0x04B8-0x04BF */ 
-0x04C0, 0x04C2, 0x04C2, 0x04C4, 0x04C4, 0x04C5, 0x04C6, 0x04C8,   /* 0x04C0-0x04C7 */ 
-0x04C8, 0x04C9, 0x04CA, 0x04CC, 0x04CC, 0x04CD, 0x04CE, 0x04CF,   /* 0x04C8-0x04CF */ 
-0x04D1, 0x04D1, 0x04D3, 0x04D3, 0x04D5, 0x04D5, 0x04D7, 0x04D7,   /* 0x04D0-0x04D7 */ 
-0x04D9, 0x04D9, 0x04DB, 0x04DB, 0x04DD, 0x04DD, 0x04DF, 0x04DF,   /* 0x04D8-0x04DF */ 
-0x04E1, 0x04E1, 0x04E3, 0x04E3, 0x04E5, 0x04E5, 0x04E7, 0x04E7,   /* 0x04E0-0x04E7 */ 
-0x04E9, 0x04E9, 0x04EB, 0x04EB, 0x04EC, 0x04ED, 0x04EF, 0x04EF,   /* 0x04E8-0x04EF */ 
-0x04F1, 0x04F1, 0x04F3, 0x04F3, 0x04F5, 0x04F5, 0x04F6, 0x04F7,   /* 0x04F0-0x04F7 */ 
-0x04F9, 0x04F9, 0x04FA, 0x04FB, 0x04FC, 0x04FD, 0x04FE, 0x04FF,   /* 0x04F8-0x04FF */ 
-0x0500, 0x0501, 0x0502, 0x0503, 0x0504, 0x0505, 0x0506, 0x0507,   /* 0x0500-0x0507 */ 
-0x0508, 0x0509, 0x050A, 0x050B, 0x050C, 0x050D, 0x050E, 0x050F,   /* 0x0508-0x050F */ 
-0x0510, 0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0516, 0x0517,   /* 0x0510-0x0517 */ 
-0x0518, 0x0519, 0x051A, 0x051B, 0x051C, 0x051D, 0x051E, 0x051F,   /* 0x0518-0x051F */ 
-0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527,   /* 0x0520-0x0527 */ 
-0x0528, 0x0529, 0x052A, 0x052B, 0x052C, 0x052D, 0x052E, 0x052F,   /* 0x0528-0x052F */ 
-0x0530, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567,   /* 0x0530-0x0537 */ 
-0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F,   /* 0x0538-0x053F */ 
-0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577,   /* 0x0540-0x0547 */ 
-0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F,   /* 0x0548-0x054F */ 
-0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0557,   /* 0x0550-0x0557 */ 
-0x0558, 0x0559, 0x055A, 0x055B, 0x055C, 0x055D, 0x055E, 0x055F,   /* 0x0558-0x055F */ 
-0x0560, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567,   /* 0x0560-0x0567 */ 
-0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F,   /* 0x0568-0x056F */ 
-0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577,   /* 0x0570-0x0577 */ 
-0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F }; /* 0x0578-0x057F */
-
-static const u_int16_t lowcase_table_4[512] = {
-0x1E01, 0x1E01, 0x1E03, 0x1E03, 0x1E05, 0x1E05, 0x1E07, 0x1E07,   /* 0x1E00-0x1E07 */ 
-0x1E09, 0x1E09, 0x1E0B, 0x1E0B, 0x1E0D, 0x1E0D, 0x1E0F, 0x1E0F,   /* 0x1E08-0x1E0F */ 
-0x1E11, 0x1E11, 0x1E13, 0x1E13, 0x1E15, 0x1E15, 0x1E17, 0x1E17,   /* 0x1E10-0x1E17 */ 
-0x1E19, 0x1E19, 0x1E1B, 0x1E1B, 0x1E1D, 0x1E1D, 0x1E1F, 0x1E1F,   /* 0x1E18-0x1E1F */ 
-0x1E21, 0x1E21, 0x1E23, 0x1E23, 0x1E25, 0x1E25, 0x1E27, 0x1E27,   /* 0x1E20-0x1E27 */ 
-0x1E29, 0x1E29, 0x1E2B, 0x1E2B, 0x1E2D, 0x1E2D, 0x1E2F, 0x1E2F,   /* 0x1E28-0x1E2F */ 
-0x1E31, 0x1E31, 0x1E33, 0x1E33, 0x1E35, 0x1E35, 0x1E37, 0x1E37,   /* 0x1E30-0x1E37 */ 
-0x1E39, 0x1E39, 0x1E3B, 0x1E3B, 0x1E3D, 0x1E3D, 0x1E3F, 0x1E3F,   /* 0x1E38-0x1E3F */ 
-0x1E41, 0x1E41, 0x1E43, 0x1E43, 0x1E45, 0x1E45, 0x1E47, 0x1E47,   /* 0x1E40-0x1E47 */ 
-0x1E49, 0x1E49, 0x1E4B, 0x1E4B, 0x1E4D, 0x1E4D, 0x1E4F, 0x1E4F,   /* 0x1E48-0x1E4F */ 
-0x1E51, 0x1E51, 0x1E53, 0x1E53, 0x1E55, 0x1E55, 0x1E57, 0x1E57,   /* 0x1E50-0x1E57 */ 
-0x1E59, 0x1E59, 0x1E5B, 0x1E5B, 0x1E5D, 0x1E5D, 0x1E5F, 0x1E5F,   /* 0x1E58-0x1E5F */ 
-0x1E61, 0x1E61, 0x1E63, 0x1E63, 0x1E65, 0x1E65, 0x1E67, 0x1E67,   /* 0x1E60-0x1E67 */ 
-0x1E69, 0x1E69, 0x1E6B, 0x1E6B, 0x1E6D, 0x1E6D, 0x1E6F, 0x1E6F,   /* 0x1E68-0x1E6F */ 
-0x1E71, 0x1E71, 0x1E73, 0x1E73, 0x1E75, 0x1E75, 0x1E77, 0x1E77,   /* 0x1E70-0x1E77 */ 
-0x1E79, 0x1E79, 0x1E7B, 0x1E7B, 0x1E7D, 0x1E7D, 0x1E7F, 0x1E7F,   /* 0x1E78-0x1E7F */ 
-0x1E81, 0x1E81, 0x1E83, 0x1E83, 0x1E85, 0x1E85, 0x1E87, 0x1E87,   /* 0x1E80-0x1E87 */ 
-0x1E89, 0x1E89, 0x1E8B, 0x1E8B, 0x1E8D, 0x1E8D, 0x1E8F, 0x1E8F,   /* 0x1E88-0x1E8F */ 
-0x1E91, 0x1E91, 0x1E93, 0x1E93, 0x1E95, 0x1E95, 0x1E96, 0x1E97,   /* 0x1E90-0x1E97 */ 
-0x1E98, 0x1E99, 0x1E9A, 0x1E9B, 0x1E9C, 0x1E9D, 0x1E9E, 0x1E9F,   /* 0x1E98-0x1E9F */ 
-0x1EA1, 0x1EA1, 0x1EA3, 0x1EA3, 0x1EA5, 0x1EA5, 0x1EA7, 0x1EA7,   /* 0x1EA0-0x1EA7 */ 
-0x1EA9, 0x1EA9, 0x1EAB, 0x1EAB, 0x1EAD, 0x1EAD, 0x1EAF, 0x1EAF,   /* 0x1EA8-0x1EAF */ 
-0x1EB1, 0x1EB1, 0x1EB3, 0x1EB3, 0x1EB5, 0x1EB5, 0x1EB7, 0x1EB7,   /* 0x1EB0-0x1EB7 */ 
-0x1EB9, 0x1EB9, 0x1EBB, 0x1EBB, 0x1EBD, 0x1EBD, 0x1EBF, 0x1EBF,   /* 0x1EB8-0x1EBF */ 
-0x1EC1, 0x1EC1, 0x1EC3, 0x1EC3, 0x1EC5, 0x1EC5, 0x1EC7, 0x1EC7,   /* 0x1EC0-0x1EC7 */ 
-0x1EC9, 0x1EC9, 0x1ECB, 0x1ECB, 0x1ECD, 0x1ECD, 0x1ECF, 0x1ECF,   /* 0x1EC8-0x1ECF */ 
-0x1ED1, 0x1ED1, 0x1ED3, 0x1ED3, 0x1ED5, 0x1ED5, 0x1ED7, 0x1ED7,   /* 0x1ED0-0x1ED7 */ 
-0x1ED9, 0x1ED9, 0x1EDB, 0x1EDB, 0x1EDD, 0x1EDD, 0x1EDF, 0x1EDF,   /* 0x1ED8-0x1EDF */ 
-0x1EE1, 0x1EE1, 0x1EE3, 0x1EE3, 0x1EE5, 0x1EE5, 0x1EE7, 0x1EE7,   /* 0x1EE0-0x1EE7 */ 
-0x1EE9, 0x1EE9, 0x1EEB, 0x1EEB, 0x1EED, 0x1EED, 0x1EEF, 0x1EEF,   /* 0x1EE8-0x1EEF */ 
-0x1EF1, 0x1EF1, 0x1EF3, 0x1EF3, 0x1EF5, 0x1EF5, 0x1EF7, 0x1EF7,   /* 0x1EF0-0x1EF7 */ 
-0x1EF9, 0x1EF9, 0x1EFA, 0x1EFB, 0x1EFC, 0x1EFD, 0x1EFE, 0x1EFF,   /* 0x1EF8-0x1EFF */ 
-0x1F00, 0x1F01, 0x1F02, 0x1F03, 0x1F04, 0x1F05, 0x1F06, 0x1F07,   /* 0x1F00-0x1F07 */ 
-0x1F00, 0x1F01, 0x1F02, 0x1F03, 0x1F04, 0x1F05, 0x1F06, 0x1F07,   /* 0x1F08-0x1F0F */ 
-0x1F10, 0x1F11, 0x1F12, 0x1F13, 0x1F14, 0x1F15, 0x1F16, 0x1F17,   /* 0x1F10-0x1F17 */ 
-0x1F10, 0x1F11, 0x1F12, 0x1F13, 0x1F14, 0x1F15, 0x1F1E, 0x1F1F,   /* 0x1F18-0x1F1F */ 
-0x1F20, 0x1F21, 0x1F22, 0x1F23, 0x1F24, 0x1F25, 0x1F26, 0x1F27,   /* 0x1F20-0x1F27 */ 
-0x1F20, 0x1F21, 0x1F22, 0x1F23, 0x1F24, 0x1F25, 0x1F26, 0x1F27,   /* 0x1F28-0x1F2F */ 
-0x1F30, 0x1F31, 0x1F32, 0x1F33, 0x1F34, 0x1F35, 0x1F36, 0x1F37,   /* 0x1F30-0x1F37 */ 
-0x1F30, 0x1F31, 0x1F32, 0x1F33, 0x1F34, 0x1F35, 0x1F36, 0x1F37,   /* 0x1F38-0x1F3F */ 
-0x1F40, 0x1F41, 0x1F42, 0x1F43, 0x1F44, 0x1F45, 0x1F46, 0x1F47,   /* 0x1F40-0x1F47 */ 
-0x1F40, 0x1F41, 0x1F42, 0x1F43, 0x1F44, 0x1F45, 0x1F4E, 0x1F4F,   /* 0x1F48-0x1F4F */ 
-0x1F50, 0x1F51, 0x1F52, 0x1F53, 0x1F54, 0x1F55, 0x1F56, 0x1F57,   /* 0x1F50-0x1F57 */ 
-0x1F58, 0x1F51, 0x1F5A, 0x1F53, 0x1F5C, 0x1F55, 0x1F5E, 0x1F57,   /* 0x1F58-0x1F5F */ 
-0x1F60, 0x1F61, 0x1F62, 0x1F63, 0x1F64, 0x1F65, 0x1F66, 0x1F67,   /* 0x1F60-0x1F67 */ 
-0x1F60, 0x1F61, 0x1F62, 0x1F63, 0x1F64, 0x1F65, 0x1F66, 0x1F67,   /* 0x1F68-0x1F6F */ 
-0x1F70, 0x1F71, 0x1F72, 0x1F73, 0x1F74, 0x1F75, 0x1F76, 0x1F77,   /* 0x1F70-0x1F77 */ 
-0x1F78, 0x1F79, 0x1F7A, 0x1F7B, 0x1F7C, 0x1F7D, 0x1F7E, 0x1F7F,   /* 0x1F78-0x1F7F */ 
-0x1F80, 0x1F81, 0x1F82, 0x1F83, 0x1F84, 0x1F85, 0x1F86, 0x1F87,   /* 0x1F80-0x1F87 */ 
-0x1F88, 0x1F89, 0x1F8A, 0x1F8B, 0x1F8C, 0x1F8D, 0x1F8E, 0x1F8F,   /* 0x1F88-0x1F8F */ 
-0x1F90, 0x1F91, 0x1F92, 0x1F93, 0x1F94, 0x1F95, 0x1F96, 0x1F97,   /* 0x1F90-0x1F97 */ 
-0x1F98, 0x1F99, 0x1F9A, 0x1F9B, 0x1F9C, 0x1F9D, 0x1F9E, 0x1F9F,   /* 0x1F98-0x1F9F */ 
-0x1FA0, 0x1FA1, 0x1FA2, 0x1FA3, 0x1FA4, 0x1FA5, 0x1FA6, 0x1FA7,   /* 0x1FA0-0x1FA7 */ 
-0x1FA8, 0x1FA9, 0x1FAA, 0x1FAB, 0x1FAC, 0x1FAD, 0x1FAE, 0x1FAF,   /* 0x1FA8-0x1FAF */ 
-0x1FB0, 0x1FB1, 0x1FB2, 0x1FB3, 0x1FB4, 0x1FB5, 0x1FB6, 0x1FB7,   /* 0x1FB0-0x1FB7 */ 
-0x1FB0, 0x1FB1, 0x1F70, 0x1F71, 0x1FBC, 0x1FBD, 0x1FBE, 0x1FBF,   /* 0x1FB8-0x1FBF */ 
-0x1FC0, 0x1FC1, 0x1FC2, 0x1FC3, 0x1FC4, 0x1FC5, 0x1FC6, 0x1FC7,   /* 0x1FC0-0x1FC7 */ 
-0x1F72, 0x1F73, 0x1F74, 0x1F75, 0x1FCC, 0x1FCD, 0x1FCE, 0x1FCF,   /* 0x1FC8-0x1FCF */ 
-0x1FD0, 0x1FD1, 0x1FD2, 0x1FD3, 0x1FD4, 0x1FD5, 0x1FD6, 0x1FD7,   /* 0x1FD0-0x1FD7 */ 
-0x1FD0, 0x1FD1, 0x1F76, 0x1F77, 0x1FDC, 0x1FDD, 0x1FDE, 0x1FDF,   /* 0x1FD8-0x1FDF */ 
-0x1FE0, 0x1FE1, 0x1FE2, 0x1FE3, 0x1FE4, 0x1FE5, 0x1FE6, 0x1FE7,   /* 0x1FE0-0x1FE7 */ 
-0x1FE0, 0x1FE1, 0x1F7A, 0x1F7B, 0x1FE5, 0x1FED, 0x1FEE, 0x1FEF,   /* 0x1FE8-0x1FEF */ 
-0x1FF0, 0x1FF1, 0x1FF2, 0x1FF3, 0x1FF4, 0x1FF5, 0x1FF6, 0x1FF7,   /* 0x1FF0-0x1FF7 */ 
-0x1F78, 0x1F79, 0x1F7C, 0x1F7D, 0x1FFC, 0x1FFD, 0x1FFE, 0x1FFF }; /* 0x1FF8-0x1FFF */
-
-static const u_int16_t lowcase_table_5[64] = {
-0x2140, 0x2141, 0x2142, 0x2143, 0x2144, 0x2145, 0x2146, 0x2147,   /* 0x2140-0x2147 */ 
-0x2148, 0x2149, 0x214A, 0x214B, 0x214C, 0x214D, 0x214E, 0x214F,   /* 0x2148-0x214F */ 
-0x2150, 0x2151, 0x2152, 0x2153, 0x2154, 0x2155, 0x2156, 0x2157,   /* 0x2150-0x2157 */ 
-0x2158, 0x2159, 0x215A, 0x215B, 0x215C, 0x215D, 0x215E, 0x215F,   /* 0x2158-0x215F */ 
-0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177,   /* 0x2160-0x2167 */ 
-0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F,   /* 0x2168-0x216F */ 
-0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177,   /* 0x2170-0x2177 */ 
-0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F }; /* 0x2178-0x217F */
-
-static const u_int16_t lowcase_table_6[128] = {
-0x2480, 0x2481, 0x2482, 0x2483, 0x2484, 0x2485, 0x2486, 0x2487,   /* 0x2480-0x2487 */ 
-0x2488, 0x2489, 0x248A, 0x248B, 0x248C, 0x248D, 0x248E, 0x248F,   /* 0x2488-0x248F */ 
-0x2490, 0x2491, 0x2492, 0x2493, 0x2494, 0x2495, 0x2496, 0x2497,   /* 0x2490-0x2497 */ 
-0x2498, 0x2499, 0x249A, 0x249B, 0x249C, 0x249D, 0x249E, 0x249F,   /* 0x2498-0x249F */ 
-0x24A0, 0x24A1, 0x24A2, 0x24A3, 0x24A4, 0x24A5, 0x24A6, 0x24A7,   /* 0x24A0-0x24A7 */ 
-0x24A8, 0x24A9, 0x24AA, 0x24AB, 0x24AC, 0x24AD, 0x24AE, 0x24AF,   /* 0x24A8-0x24AF */ 
-0x24B0, 0x24B1, 0x24B2, 0x24B3, 0x24B4, 0x24B5, 0x24D0, 0x24D1,   /* 0x24B0-0x24B7 */ 
-0x24D2, 0x24D3, 0x24D4, 0x24D5, 0x24D6, 0x24D7, 0x24D8, 0x24D9,   /* 0x24B8-0x24BF */ 
-0x24DA, 0x24DB, 0x24DC, 0x24DD, 0x24DE, 0x24DF, 0x24E0, 0x24E1,   /* 0x24C0-0x24C7 */ 
-0x24E2, 0x24E3, 0x24E4, 0x24E5, 0x24E6, 0x24E7, 0x24E8, 0x24E9,   /* 0x24C8-0x24CF */ 
-0x24D0, 0x24D1, 0x24D2, 0x24D3, 0x24D4, 0x24D5, 0x24D6, 0x24D7,   /* 0x24D0-0x24D7 */ 
-0x24D8, 0x24D9, 0x24DA, 0x24DB, 0x24DC, 0x24DD, 0x24DE, 0x24DF,   /* 0x24D8-0x24DF */ 
-0x24E0, 0x24E1, 0x24E2, 0x24E3, 0x24E4, 0x24E5, 0x24E6, 0x24E7,   /* 0x24E0-0x24E7 */ 
-0x24E8, 0x24E9, 0x24EA, 0x24EB, 0x24EC, 0x24ED, 0x24EE, 0x24EF,   /* 0x24E8-0x24EF */ 
-0x24F0, 0x24F1, 0x24F2, 0x24F3, 0x24F4, 0x24F5, 0x24F6, 0x24F7,   /* 0x24F0-0x24F7 */ 
-0x24F8, 0x24F9, 0x24FA, 0x24FB, 0x24FC, 0x24FD, 0x24FE, 0x24FF }; /* 0x24F8-0x24FF */
-
-static const u_int16_t lowcase_table_7[64] = {
-0xFF00, 0xFF01, 0xFF02, 0xFF03, 0xFF04, 0xFF05, 0xFF06, 0xFF07,   /* 0xFF00-0xFF07 */ 
-0xFF08, 0xFF09, 0xFF0A, 0xFF0B, 0xFF0C, 0xFF0D, 0xFF0E, 0xFF0F,   /* 0xFF08-0xFF0F */ 
-0xFF10, 0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17,   /* 0xFF10-0xFF17 */ 
-0xFF18, 0xFF19, 0xFF1A, 0xFF1B, 0xFF1C, 0xFF1D, 0xFF1E, 0xFF1F,   /* 0xFF18-0xFF1F */ 
-0xFF20, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47,   /* 0xFF20-0xFF27 */ 
-0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F,   /* 0xFF28-0xFF2F */ 
-0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57,   /* 0xFF30-0xFF37 */ 
-0xFF58, 0xFF59, 0xFF5A, 0xFF3B, 0xFF3C, 0xFF3D, 0xFF3E, 0xFF3F }; /* 0xFF38-0xFF3F */
-
diff --git a/libatalk/unicode/utf16_case.c b/libatalk/unicode/utf16_case.c
new file mode 100644 (file)
index 0000000..5b23ee3
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+DO NOT EDIT BY HAND!!!
+
+This file is generated by
+ contrib/misc/make-casetable.pl UnicodeData.txt utf16_casetable.h utf16_case.c
+
+UnicodeData.txt is got from
+http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
+*/
+
+#include <netatalk/endian.h>
+#include <atalk/unicode.h>
+#include "utf16_casetable.h"
+
+/*******************************************************************
+ Convert a wide character to upper case.
+*******************************************************************/
+ucs2_t toupper_w(ucs2_t val)
+{
+    if ( val <= 0x02BF)
+        return upper_table_1[val];
+
+    if ( val >= 0x0340 && val <= 0x05BF)
+        return upper_table_2[val-0x0340];
+
+    if ( val >= 0x1D40 && val <= 0x1D7F)
+        return upper_table_3[val-0x1D40];
+
+    if ( val >= 0x1E00 && val <= 0x1FFF)
+        return upper_table_4[val-0x1E00];
+
+    if ( val >= 0x2140 && val <= 0x21BF)
+        return upper_table_5[val-0x2140];
+
+    if ( val >= 0x24C0 && val <= 0x24FF)
+        return upper_table_6[val-0x24C0];
+
+    if ( val >= 0x2C00 && val <= 0x2D3F)
+        return upper_table_7[val-0x2C00];
+
+    if ( val >= 0xA640 && val <= 0xA6BF)
+        return upper_table_8[val-0xA640];
+
+    if ( val >= 0xA700 && val <= 0xA7BF)
+        return upper_table_9[val-0xA700];
+
+    if ( val >= 0xFF40 && val <= 0xFF7F)
+        return upper_table_10[val-0xFF40];
+
+       return (val);
+}
+
+/*******************************************************************
+ Convert a surrogate pair to upper case.
+*******************************************************************/
+u_int32_t toupper_sp(u_int32_t val)
+{
+    if ( val >= 0xD801DC00 && val <= 0xD801DC7F)
+        return upper_table_sp_1[val-0xD801DC00];
+
+       return (val);
+}
+
+/*******************************************************************
+ Convert a wide character to lower case.
+*******************************************************************/
+ucs2_t tolower_w(ucs2_t val)
+{
+    if ( val <= 0x007F)
+        return lower_table_1[val];
+
+    if ( val >= 0x00C0 && val <= 0x027F)
+        return lower_table_2[val-0x00C0];
+
+    if ( val >= 0x0340 && val <= 0x057F)
+        return lower_table_3[val-0x0340];
+
+    if ( val >= 0x1080 && val <= 0x10FF)
+        return lower_table_4[val-0x1080];
+
+    if ( val >= 0x1E00 && val <= 0x1FFF)
+        return lower_table_5[val-0x1E00];
+
+    if ( val >= 0x2100 && val <= 0x21BF)
+        return lower_table_6[val-0x2100];
+
+    if ( val >= 0x2480 && val <= 0x24FF)
+        return lower_table_7[val-0x2480];
+
+    if ( val >= 0x2C00 && val <= 0x2CFF)
+        return lower_table_8[val-0x2C00];
+
+    if ( val >= 0xA640 && val <= 0xA6BF)
+        return lower_table_9[val-0xA640];
+
+    if ( val >= 0xA700 && val <= 0xA7BF)
+        return lower_table_10[val-0xA700];
+
+    if ( val >= 0xFF00 && val <= 0xFF3F)
+        return lower_table_11[val-0xFF00];
+
+       return (val);
+}
+
+/*******************************************************************
+ Convert a surrogate pair to lower case.
+*******************************************************************/
+u_int32_t tolower_sp(u_int32_t val)
+{
+    if ( val >= 0xD801DC00 && val <= 0xD801DC3F)
+        return lower_table_sp_1[val-0xD801DC00];
+
+       return (val);
+}
+
+/* EOF */
diff --git a/libatalk/unicode/utf16_casetable.h b/libatalk/unicode/utf16_casetable.h
new file mode 100644 (file)
index 0000000..9a1366e
--- /dev/null
@@ -0,0 +1,5840 @@
+/*
+DO NOT EDIT BY HAND!!!
+
+This file is generated by
+ contrib/misc/make-casetable.pl UnicodeData.txt utf16_casetable.h utf16_case.c
+
+UnicodeData.txt is got from
+http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
+*/
+
+static const u_int16_t upper_table_1[704] = {
+  0x0000, /*U+0000*/ /**/
+  0x0001, /*U+0001*/ /**/
+  0x0002, /*U+0002*/ /**/
+  0x0003, /*U+0003*/ /**/
+  0x0004, /*U+0004*/ /**/
+  0x0005, /*U+0005*/ /**/
+  0x0006, /*U+0006*/ /**/
+  0x0007, /*U+0007*/ /**/
+  0x0008, /*U+0008*/ /**/
+  0x0009, /*U+0009*/ /**/
+  0x000A, /*U+000A*/ /**/
+  0x000B, /*U+000B*/ /**/
+  0x000C, /*U+000C*/ /**/
+  0x000D, /*U+000D*/ /**/
+  0x000E, /*U+000E*/ /**/
+  0x000F, /*U+000F*/ /**/
+  0x0010, /*U+0010*/ /**/
+  0x0011, /*U+0011*/ /**/
+  0x0012, /*U+0012*/ /**/
+  0x0013, /*U+0013*/ /**/
+  0x0014, /*U+0014*/ /**/
+  0x0015, /*U+0015*/ /**/
+  0x0016, /*U+0016*/ /**/
+  0x0017, /*U+0017*/ /**/
+  0x0018, /*U+0018*/ /**/
+  0x0019, /*U+0019*/ /**/
+  0x001A, /*U+001A*/ /**/
+  0x001B, /*U+001B*/ /**/
+  0x001C, /*U+001C*/ /**/
+  0x001D, /*U+001D*/ /**/
+  0x001E, /*U+001E*/ /**/
+  0x001F, /*U+001F*/ /**/
+  0x0020, /*U+0020*/ /**/
+  0x0021, /*U+0021*/ /**/
+  0x0022, /*U+0022*/ /**/
+  0x0023, /*U+0023*/ /**/
+  0x0024, /*U+0024*/ /**/
+  0x0025, /*U+0025*/ /**/
+  0x0026, /*U+0026*/ /**/
+  0x0027, /*U+0027*/ /**/
+  0x0028, /*U+0028*/ /**/
+  0x0029, /*U+0029*/ /**/
+  0x002A, /*U+002A*/ /**/
+  0x002B, /*U+002B*/ /**/
+  0x002C, /*U+002C*/ /**/
+  0x002D, /*U+002D*/ /**/
+  0x002E, /*U+002E*/ /**/
+  0x002F, /*U+002F*/ /**/
+  0x0030, /*U+0030*/ /**/
+  0x0031, /*U+0031*/ /**/
+  0x0032, /*U+0032*/ /**/
+  0x0033, /*U+0033*/ /**/
+  0x0034, /*U+0034*/ /**/
+  0x0035, /*U+0035*/ /**/
+  0x0036, /*U+0036*/ /**/
+  0x0037, /*U+0037*/ /**/
+  0x0038, /*U+0038*/ /**/
+  0x0039, /*U+0039*/ /**/
+  0x003A, /*U+003A*/ /**/
+  0x003B, /*U+003B*/ /**/
+  0x003C, /*U+003C*/ /**/
+  0x003D, /*U+003D*/ /**/
+  0x003E, /*U+003E*/ /**/
+  0x003F, /*U+003F*/ /**/
+  0x0040, /*U+0040*/ /**/
+  0x0041, /*U+0041*/ /**/
+  0x0042, /*U+0042*/ /**/
+  0x0043, /*U+0043*/ /**/
+  0x0044, /*U+0044*/ /**/
+  0x0045, /*U+0045*/ /**/
+  0x0046, /*U+0046*/ /**/
+  0x0047, /*U+0047*/ /**/
+  0x0048, /*U+0048*/ /**/
+  0x0049, /*U+0049*/ /**/
+  0x004A, /*U+004A*/ /**/
+  0x004B, /*U+004B*/ /**/
+  0x004C, /*U+004C*/ /**/
+  0x004D, /*U+004D*/ /**/
+  0x004E, /*U+004E*/ /**/
+  0x004F, /*U+004F*/ /**/
+  0x0050, /*U+0050*/ /**/
+  0x0051, /*U+0051*/ /**/
+  0x0052, /*U+0052*/ /**/
+  0x0053, /*U+0053*/ /**/
+  0x0054, /*U+0054*/ /**/
+  0x0055, /*U+0055*/ /**/
+  0x0056, /*U+0056*/ /**/
+  0x0057, /*U+0057*/ /**/
+  0x0058, /*U+0058*/ /**/
+  0x0059, /*U+0059*/ /**/
+  0x005A, /*U+005A*/ /**/
+  0x005B, /*U+005B*/ /**/
+  0x005C, /*U+005C*/ /**/
+  0x005D, /*U+005D*/ /**/
+  0x005E, /*U+005E*/ /**/
+  0x005F, /*U+005F*/ /**/
+  0x0060, /*U+0060*/ /**/
+  0x0041, /*U+0061*/ /*LATIN SMALL LETTER A*/
+  0x0042, /*U+0062*/ /*LATIN SMALL LETTER B*/
+  0x0043, /*U+0063*/ /*LATIN SMALL LETTER C*/
+  0x0044, /*U+0064*/ /*LATIN SMALL LETTER D*/
+  0x0045, /*U+0065*/ /*LATIN SMALL LETTER E*/
+  0x0046, /*U+0066*/ /*LATIN SMALL LETTER F*/
+  0x0047, /*U+0067*/ /*LATIN SMALL LETTER G*/
+  0x0048, /*U+0068*/ /*LATIN SMALL LETTER H*/
+  0x0049, /*U+0069*/ /*LATIN SMALL LETTER I*/
+  0x004A, /*U+006A*/ /*LATIN SMALL LETTER J*/
+  0x004B, /*U+006B*/ /*LATIN SMALL LETTER K*/
+  0x004C, /*U+006C*/ /*LATIN SMALL LETTER L*/
+  0x004D, /*U+006D*/ /*LATIN SMALL LETTER M*/
+  0x004E, /*U+006E*/ /*LATIN SMALL LETTER N*/
+  0x004F, /*U+006F*/ /*LATIN SMALL LETTER O*/
+  0x0050, /*U+0070*/ /*LATIN SMALL LETTER P*/
+  0x0051, /*U+0071*/ /*LATIN SMALL LETTER Q*/
+  0x0052, /*U+0072*/ /*LATIN SMALL LETTER R*/
+  0x0053, /*U+0073*/ /*LATIN SMALL LETTER S*/
+  0x0054, /*U+0074*/ /*LATIN SMALL LETTER T*/
+  0x0055, /*U+0075*/ /*LATIN SMALL LETTER U*/
+  0x0056, /*U+0076*/ /*LATIN SMALL LETTER V*/
+  0x0057, /*U+0077*/ /*LATIN SMALL LETTER W*/
+  0x0058, /*U+0078*/ /*LATIN SMALL LETTER X*/
+  0x0059, /*U+0079*/ /*LATIN SMALL LETTER Y*/
+  0x005A, /*U+007A*/ /*LATIN SMALL LETTER Z*/
+  0x007B, /*U+007B*/ /**/
+  0x007C, /*U+007C*/ /**/
+  0x007D, /*U+007D*/ /**/
+  0x007E, /*U+007E*/ /**/
+  0x007F, /*U+007F*/ /**/
+  0x0080, /*U+0080*/ /**/
+  0x0081, /*U+0081*/ /**/
+  0x0082, /*U+0082*/ /**/
+  0x0083, /*U+0083*/ /**/
+  0x0084, /*U+0084*/ /**/
+  0x0085, /*U+0085*/ /**/
+  0x0086, /*U+0086*/ /**/
+  0x0087, /*U+0087*/ /**/
+  0x0088, /*U+0088*/ /**/
+  0x0089, /*U+0089*/ /**/
+  0x008A, /*U+008A*/ /**/
+  0x008B, /*U+008B*/ /**/
+  0x008C, /*U+008C*/ /**/
+  0x008D, /*U+008D*/ /**/
+  0x008E, /*U+008E*/ /**/
+  0x008F, /*U+008F*/ /**/
+  0x0090, /*U+0090*/ /**/
+  0x0091, /*U+0091*/ /**/
+  0x0092, /*U+0092*/ /**/
+  0x0093, /*U+0093*/ /**/
+  0x0094, /*U+0094*/ /**/
+  0x0095, /*U+0095*/ /**/
+  0x0096, /*U+0096*/ /**/
+  0x0097, /*U+0097*/ /**/
+  0x0098, /*U+0098*/ /**/
+  0x0099, /*U+0099*/ /**/
+  0x009A, /*U+009A*/ /**/
+  0x009B, /*U+009B*/ /**/
+  0x009C, /*U+009C*/ /**/
+  0x009D, /*U+009D*/ /**/
+  0x009E, /*U+009E*/ /**/
+  0x009F, /*U+009F*/ /**/
+  0x00A0, /*U+00A0*/ /**/
+  0x00A1, /*U+00A1*/ /**/
+  0x00A2, /*U+00A2*/ /**/
+  0x00A3, /*U+00A3*/ /**/
+  0x00A4, /*U+00A4*/ /**/
+  0x00A5, /*U+00A5*/ /**/
+  0x00A6, /*U+00A6*/ /**/
+  0x00A7, /*U+00A7*/ /**/
+  0x00A8, /*U+00A8*/ /**/
+  0x00A9, /*U+00A9*/ /**/
+  0x00AA, /*U+00AA*/ /**/
+  0x00AB, /*U+00AB*/ /**/
+  0x00AC, /*U+00AC*/ /**/
+  0x00AD, /*U+00AD*/ /**/
+  0x00AE, /*U+00AE*/ /**/
+  0x00AF, /*U+00AF*/ /**/
+  0x00B0, /*U+00B0*/ /**/
+  0x00B1, /*U+00B1*/ /**/
+  0x00B2, /*U+00B2*/ /**/
+  0x00B3, /*U+00B3*/ /**/
+  0x00B4, /*U+00B4*/ /**/
+  0x039C, /*U+00B5*/ /*MICRO SIGN*/
+  0x00B6, /*U+00B6*/ /**/
+  0x00B7, /*U+00B7*/ /**/
+  0x00B8, /*U+00B8*/ /**/
+  0x00B9, /*U+00B9*/ /**/
+  0x00BA, /*U+00BA*/ /**/
+  0x00BB, /*U+00BB*/ /**/
+  0x00BC, /*U+00BC*/ /**/
+  0x00BD, /*U+00BD*/ /**/
+  0x00BE, /*U+00BE*/ /**/
+  0x00BF, /*U+00BF*/ /**/
+  0x00C0, /*U+00C0*/ /**/
+  0x00C1, /*U+00C1*/ /**/
+  0x00C2, /*U+00C2*/ /**/
+  0x00C3, /*U+00C3*/ /**/
+  0x00C4, /*U+00C4*/ /**/
+  0x00C5, /*U+00C5*/ /**/
+  0x00C6, /*U+00C6*/ /**/
+  0x00C7, /*U+00C7*/ /**/
+  0x00C8, /*U+00C8*/ /**/
+  0x00C9, /*U+00C9*/ /**/
+  0x00CA, /*U+00CA*/ /**/
+  0x00CB, /*U+00CB*/ /**/
+  0x00CC, /*U+00CC*/ /**/
+  0x00CD, /*U+00CD*/ /**/
+  0x00CE, /*U+00CE*/ /**/
+  0x00CF, /*U+00CF*/ /**/
+  0x00D0, /*U+00D0*/ /**/
+  0x00D1, /*U+00D1*/ /**/
+  0x00D2, /*U+00D2*/ /**/
+  0x00D3, /*U+00D3*/ /**/
+  0x00D4, /*U+00D4*/ /**/
+  0x00D5, /*U+00D5*/ /**/
+  0x00D6, /*U+00D6*/ /**/
+  0x00D7, /*U+00D7*/ /**/
+  0x00D8, /*U+00D8*/ /**/
+  0x00D9, /*U+00D9*/ /**/
+  0x00DA, /*U+00DA*/ /**/
+  0x00DB, /*U+00DB*/ /**/
+  0x00DC, /*U+00DC*/ /**/
+  0x00DD, /*U+00DD*/ /**/
+  0x00DE, /*U+00DE*/ /**/
+  0x00DF, /*U+00DF*/ /**/
+  0x00C0, /*U+00E0*/ /*LATIN SMALL LETTER A WITH GRAVE*/
+  0x00C1, /*U+00E1*/ /*LATIN SMALL LETTER A WITH ACUTE*/
+  0x00C2, /*U+00E2*/ /*LATIN SMALL LETTER A WITH CIRCUMFLEX*/
+  0x00C3, /*U+00E3*/ /*LATIN SMALL LETTER A WITH TILDE*/
+  0x00C4, /*U+00E4*/ /*LATIN SMALL LETTER A WITH DIAERESIS*/
+  0x00C5, /*U+00E5*/ /*LATIN SMALL LETTER A WITH RING ABOVE*/
+  0x00C6, /*U+00E6*/ /*LATIN SMALL LETTER AE*/
+  0x00C7, /*U+00E7*/ /*LATIN SMALL LETTER C WITH CEDILLA*/
+  0x00C8, /*U+00E8*/ /*LATIN SMALL LETTER E WITH GRAVE*/
+  0x00C9, /*U+00E9*/ /*LATIN SMALL LETTER E WITH ACUTE*/
+  0x00CA, /*U+00EA*/ /*LATIN SMALL LETTER E WITH CIRCUMFLEX*/
+  0x00CB, /*U+00EB*/ /*LATIN SMALL LETTER E WITH DIAERESIS*/
+  0x00CC, /*U+00EC*/ /*LATIN SMALL LETTER I WITH GRAVE*/
+  0x00CD, /*U+00ED*/ /*LATIN SMALL LETTER I WITH ACUTE*/
+  0x00CE, /*U+00EE*/ /*LATIN SMALL LETTER I WITH CIRCUMFLEX*/
+  0x00CF, /*U+00EF*/ /*LATIN SMALL LETTER I WITH DIAERESIS*/
+  0x00D0, /*U+00F0*/ /*LATIN SMALL LETTER ETH*/
+  0x00D1, /*U+00F1*/ /*LATIN SMALL LETTER N WITH TILDE*/
+  0x00D2, /*U+00F2*/ /*LATIN SMALL LETTER O WITH GRAVE*/
+  0x00D3, /*U+00F3*/ /*LATIN SMALL LETTER O WITH ACUTE*/
+  0x00D4, /*U+00F4*/ /*LATIN SMALL LETTER O WITH CIRCUMFLEX*/
+  0x00D5, /*U+00F5*/ /*LATIN SMALL LETTER O WITH TILDE*/
+  0x00D6, /*U+00F6*/ /*LATIN SMALL LETTER O WITH DIAERESIS*/
+  0x00F7, /*U+00F7*/ /**/
+  0x00D8, /*U+00F8*/ /*LATIN SMALL LETTER O WITH STROKE*/
+  0x00D9, /*U+00F9*/ /*LATIN SMALL LETTER U WITH GRAVE*/
+  0x00DA, /*U+00FA*/ /*LATIN SMALL LETTER U WITH ACUTE*/
+  0x00DB, /*U+00FB*/ /*LATIN SMALL LETTER U WITH CIRCUMFLEX*/
+  0x00DC, /*U+00FC*/ /*LATIN SMALL LETTER U WITH DIAERESIS*/
+  0x00DD, /*U+00FD*/ /*LATIN SMALL LETTER Y WITH ACUTE*/
+  0x00DE, /*U+00FE*/ /*LATIN SMALL LETTER THORN*/
+  0x0178, /*U+00FF*/ /*LATIN SMALL LETTER Y WITH DIAERESIS*/
+  0x0100, /*U+0100*/ /**/
+  0x0100, /*U+0101*/ /*LATIN SMALL LETTER A WITH MACRON*/
+  0x0102, /*U+0102*/ /**/
+  0x0102, /*U+0103*/ /*LATIN SMALL LETTER A WITH BREVE*/
+  0x0104, /*U+0104*/ /**/
+  0x0104, /*U+0105*/ /*LATIN SMALL LETTER A WITH OGONEK*/
+  0x0106, /*U+0106*/ /**/
+  0x0106, /*U+0107*/ /*LATIN SMALL LETTER C WITH ACUTE*/
+  0x0108, /*U+0108*/ /**/
+  0x0108, /*U+0109*/ /*LATIN SMALL LETTER C WITH CIRCUMFLEX*/
+  0x010A, /*U+010A*/ /**/
+  0x010A, /*U+010B*/ /*LATIN SMALL LETTER C WITH DOT ABOVE*/
+  0x010C, /*U+010C*/ /**/
+  0x010C, /*U+010D*/ /*LATIN SMALL LETTER C WITH CARON*/
+  0x010E, /*U+010E*/ /**/
+  0x010E, /*U+010F*/ /*LATIN SMALL LETTER D WITH CARON*/
+  0x0110, /*U+0110*/ /**/
+  0x0110, /*U+0111*/ /*LATIN SMALL LETTER D WITH STROKE*/
+  0x0112, /*U+0112*/ /**/
+  0x0112, /*U+0113*/ /*LATIN SMALL LETTER E WITH MACRON*/
+  0x0114, /*U+0114*/ /**/
+  0x0114, /*U+0115*/ /*LATIN SMALL LETTER E WITH BREVE*/
+  0x0116, /*U+0116*/ /**/
+  0x0116, /*U+0117*/ /*LATIN SMALL LETTER E WITH DOT ABOVE*/
+  0x0118, /*U+0118*/ /**/
+  0x0118, /*U+0119*/ /*LATIN SMALL LETTER E WITH OGONEK*/
+  0x011A, /*U+011A*/ /**/
+  0x011A, /*U+011B*/ /*LATIN SMALL LETTER E WITH CARON*/
+  0x011C, /*U+011C*/ /**/
+  0x011C, /*U+011D*/ /*LATIN SMALL LETTER G WITH CIRCUMFLEX*/
+  0x011E, /*U+011E*/ /**/
+  0x011E, /*U+011F*/ /*LATIN SMALL LETTER G WITH BREVE*/
+  0x0120, /*U+0120*/ /**/
+  0x0120, /*U+0121*/ /*LATIN SMALL LETTER G WITH DOT ABOVE*/
+  0x0122, /*U+0122*/ /**/
+  0x0122, /*U+0123*/ /*LATIN SMALL LETTER G WITH CEDILLA*/
+  0x0124, /*U+0124*/ /**/
+  0x0124, /*U+0125*/ /*LATIN SMALL LETTER H WITH CIRCUMFLEX*/
+  0x0126, /*U+0126*/ /**/
+  0x0126, /*U+0127*/ /*LATIN SMALL LETTER H WITH STROKE*/
+  0x0128, /*U+0128*/ /**/
+  0x0128, /*U+0129*/ /*LATIN SMALL LETTER I WITH TILDE*/
+  0x012A, /*U+012A*/ /**/
+  0x012A, /*U+012B*/ /*LATIN SMALL LETTER I WITH MACRON*/
+  0x012C, /*U+012C*/ /**/
+  0x012C, /*U+012D*/ /*LATIN SMALL LETTER I WITH BREVE*/
+  0x012E, /*U+012E*/ /**/
+  0x012E, /*U+012F*/ /*LATIN SMALL LETTER I WITH OGONEK*/
+  0x0130, /*U+0130*/ /**/
+  0x0049, /*U+0131*/ /*LATIN SMALL LETTER DOTLESS I*/
+  0x0132, /*U+0132*/ /**/
+  0x0132, /*U+0133*/ /*LATIN SMALL LIGATURE IJ*/
+  0x0134, /*U+0134*/ /**/
+  0x0134, /*U+0135*/ /*LATIN SMALL LETTER J WITH CIRCUMFLEX*/
+  0x0136, /*U+0136*/ /**/
+  0x0136, /*U+0137*/ /*LATIN SMALL LETTER K WITH CEDILLA*/
+  0x0138, /*U+0138*/ /**/
+  0x0139, /*U+0139*/ /**/
+  0x0139, /*U+013A*/ /*LATIN SMALL LETTER L WITH ACUTE*/
+  0x013B, /*U+013B*/ /**/
+  0x013B, /*U+013C*/ /*LATIN SMALL LETTER L WITH CEDILLA*/
+  0x013D, /*U+013D*/ /**/
+  0x013D, /*U+013E*/ /*LATIN SMALL LETTER L WITH CARON*/
+  0x013F, /*U+013F*/ /**/
+  0x013F, /*U+0140*/ /*LATIN SMALL LETTER L WITH MIDDLE DOT*/
+  0x0141, /*U+0141*/ /**/
+  0x0141, /*U+0142*/ /*LATIN SMALL LETTER L WITH STROKE*/
+  0x0143, /*U+0143*/ /**/
+  0x0143, /*U+0144*/ /*LATIN SMALL LETTER N WITH ACUTE*/
+  0x0145, /*U+0145*/ /**/
+  0x0145, /*U+0146*/ /*LATIN SMALL LETTER N WITH CEDILLA*/
+  0x0147, /*U+0147*/ /**/
+  0x0147, /*U+0148*/ /*LATIN SMALL LETTER N WITH CARON*/
+  0x0149, /*U+0149*/ /**/
+  0x014A, /*U+014A*/ /**/
+  0x014A, /*U+014B*/ /*LATIN SMALL LETTER ENG*/
+  0x014C, /*U+014C*/ /**/
+  0x014C, /*U+014D*/ /*LATIN SMALL LETTER O WITH MACRON*/
+  0x014E, /*U+014E*/ /**/
+  0x014E, /*U+014F*/ /*LATIN SMALL LETTER O WITH BREVE*/
+  0x0150, /*U+0150*/ /**/
+  0x0150, /*U+0151*/ /*LATIN SMALL LETTER O WITH DOUBLE ACUTE*/
+  0x0152, /*U+0152*/ /**/
+  0x0152, /*U+0153*/ /*LATIN SMALL LIGATURE OE*/
+  0x0154, /*U+0154*/ /**/
+  0x0154, /*U+0155*/ /*LATIN SMALL LETTER R WITH ACUTE*/
+  0x0156, /*U+0156*/ /**/
+  0x0156, /*U+0157*/ /*LATIN SMALL LETTER R WITH CEDILLA*/
+  0x0158, /*U+0158*/ /**/
+  0x0158, /*U+0159*/ /*LATIN SMALL LETTER R WITH CARON*/
+  0x015A, /*U+015A*/ /**/
+  0x015A, /*U+015B*/ /*LATIN SMALL LETTER S WITH ACUTE*/
+  0x015C, /*U+015C*/ /**/
+  0x015C, /*U+015D*/ /*LATIN SMALL LETTER S WITH CIRCUMFLEX*/
+  0x015E, /*U+015E*/ /**/
+  0x015E, /*U+015F*/ /*LATIN SMALL LETTER S WITH CEDILLA*/
+  0x0160, /*U+0160*/ /**/
+  0x0160, /*U+0161*/ /*LATIN SMALL LETTER S WITH CARON*/
+  0x0162, /*U+0162*/ /**/
+  0x0162, /*U+0163*/ /*LATIN SMALL LETTER T WITH CEDILLA*/
+  0x0164, /*U+0164*/ /**/
+  0x0164, /*U+0165*/ /*LATIN SMALL LETTER T WITH CARON*/
+  0x0166, /*U+0166*/ /**/
+  0x0166, /*U+0167*/ /*LATIN SMALL LETTER T WITH STROKE*/
+  0x0168, /*U+0168*/ /**/
+  0x0168, /*U+0169*/ /*LATIN SMALL LETTER U WITH TILDE*/
+  0x016A, /*U+016A*/ /**/
+  0x016A, /*U+016B*/ /*LATIN SMALL LETTER U WITH MACRON*/
+  0x016C, /*U+016C*/ /**/
+  0x016C, /*U+016D*/ /*LATIN SMALL LETTER U WITH BREVE*/
+  0x016E, /*U+016E*/ /**/
+  0x016E, /*U+016F*/ /*LATIN SMALL LETTER U WITH RING ABOVE*/
+  0x0170, /*U+0170*/ /**/
+  0x0170, /*U+0171*/ /*LATIN SMALL LETTER U WITH DOUBLE ACUTE*/
+  0x0172, /*U+0172*/ /**/
+  0x0172, /*U+0173*/ /*LATIN SMALL LETTER U WITH OGONEK*/
+  0x0174, /*U+0174*/ /**/
+  0x0174, /*U+0175*/ /*LATIN SMALL LETTER W WITH CIRCUMFLEX*/
+  0x0176, /*U+0176*/ /**/
+  0x0176, /*U+0177*/ /*LATIN SMALL LETTER Y WITH CIRCUMFLEX*/
+  0x0178, /*U+0178*/ /**/
+  0x0179, /*U+0179*/ /**/
+  0x0179, /*U+017A*/ /*LATIN SMALL LETTER Z WITH ACUTE*/
+  0x017B, /*U+017B*/ /**/
+  0x017B, /*U+017C*/ /*LATIN SMALL LETTER Z WITH DOT ABOVE*/
+  0x017D, /*U+017D*/ /**/
+  0x017D, /*U+017E*/ /*LATIN SMALL LETTER Z WITH CARON*/
+  0x0053, /*U+017F*/ /*LATIN SMALL LETTER LONG S*/
+  0x0243, /*U+0180*/ /*LATIN SMALL LETTER B WITH STROKE*/
+  0x0181, /*U+0181*/ /**/
+  0x0182, /*U+0182*/ /**/
+  0x0182, /*U+0183*/ /*LATIN SMALL LETTER B WITH TOPBAR*/
+  0x0184, /*U+0184*/ /**/
+  0x0184, /*U+0185*/ /*LATIN SMALL LETTER TONE SIX*/
+  0x0186, /*U+0186*/ /**/
+  0x0187, /*U+0187*/ /**/
+  0x0187, /*U+0188*/ /*LATIN SMALL LETTER C WITH HOOK*/
+  0x0189, /*U+0189*/ /**/
+  0x018A, /*U+018A*/ /**/
+  0x018B, /*U+018B*/ /**/
+  0x018B, /*U+018C*/ /*LATIN SMALL LETTER D WITH TOPBAR*/
+  0x018D, /*U+018D*/ /**/
+  0x018E, /*U+018E*/ /**/
+  0x018F, /*U+018F*/ /**/
+  0x0190, /*U+0190*/ /**/
+  0x0191, /*U+0191*/ /**/
+  0x0191, /*U+0192*/ /*LATIN SMALL LETTER F WITH HOOK*/
+  0x0193, /*U+0193*/ /**/
+  0x0194, /*U+0194*/ /**/
+  0x01F6, /*U+0195*/ /*LATIN SMALL LETTER HV*/
+  0x0196, /*U+0196*/ /**/
+  0x0197, /*U+0197*/ /**/
+  0x0198, /*U+0198*/ /**/
+  0x0198, /*U+0199*/ /*LATIN SMALL LETTER K WITH HOOK*/
+  0x023D, /*U+019A*/ /*LATIN SMALL LETTER L WITH BAR*/
+  0x019B, /*U+019B*/ /**/
+  0x019C, /*U+019C*/ /**/
+  0x019D, /*U+019D*/ /**/
+  0x0220, /*U+019E*/ /*LATIN SMALL LETTER N WITH LONG RIGHT LEG*/
+  0x019F, /*U+019F*/ /**/
+  0x01A0, /*U+01A0*/ /**/
+  0x01A0, /*U+01A1*/ /*LATIN SMALL LETTER O WITH HORN*/
+  0x01A2, /*U+01A2*/ /**/
+  0x01A2, /*U+01A3*/ /*LATIN SMALL LETTER OI*/
+  0x01A4, /*U+01A4*/ /**/
+  0x01A4, /*U+01A5*/ /*LATIN SMALL LETTER P WITH HOOK*/
+  0x01A6, /*U+01A6*/ /**/
+  0x01A7, /*U+01A7*/ /**/
+  0x01A7, /*U+01A8*/ /*LATIN SMALL LETTER TONE TWO*/
+  0x01A9, /*U+01A9*/ /**/
+  0x01AA, /*U+01AA*/ /**/
+  0x01AB, /*U+01AB*/ /**/
+  0x01AC, /*U+01AC*/ /**/
+  0x01AC, /*U+01AD*/ /*LATIN SMALL LETTER T WITH HOOK*/
+  0x01AE, /*U+01AE*/ /**/
+  0x01AF, /*U+01AF*/ /**/
+  0x01AF, /*U+01B0*/ /*LATIN SMALL LETTER U WITH HORN*/
+  0x01B1, /*U+01B1*/ /**/
+  0x01B2, /*U+01B2*/ /**/
+  0x01B3, /*U+01B3*/ /**/
+  0x01B3, /*U+01B4*/ /*LATIN SMALL LETTER Y WITH HOOK*/
+  0x01B5, /*U+01B5*/ /**/
+  0x01B5, /*U+01B6*/ /*LATIN SMALL LETTER Z WITH STROKE*/
+  0x01B7, /*U+01B7*/ /**/
+  0x01B8, /*U+01B8*/ /**/
+  0x01B8, /*U+01B9*/ /*LATIN SMALL LETTER EZH REVERSED*/
+  0x01BA, /*U+01BA*/ /**/
+  0x01BB, /*U+01BB*/ /**/
+  0x01BC, /*U+01BC*/ /**/
+  0x01BC, /*U+01BD*/ /*LATIN SMALL LETTER TONE FIVE*/
+  0x01BE, /*U+01BE*/ /**/
+  0x01F7, /*U+01BF*/ /*LATIN LETTER WYNN*/
+  0x01C0, /*U+01C0*/ /**/
+  0x01C1, /*U+01C1*/ /**/
+  0x01C2, /*U+01C2*/ /**/
+  0x01C3, /*U+01C3*/ /**/
+  0x01C4, /*U+01C4*/ /**/
+  0x01C4, /*U+01C5*/ /*LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON*/
+  0x01C4, /*U+01C6*/ /*LATIN SMALL LETTER DZ WITH CARON*/
+  0x01C7, /*U+01C7*/ /**/
+  0x01C7, /*U+01C8*/ /*LATIN CAPITAL LETTER L WITH SMALL LETTER J*/
+  0x01C7, /*U+01C9*/ /*LATIN SMALL LETTER LJ*/
+  0x01CA, /*U+01CA*/ /**/
+  0x01CA, /*U+01CB*/ /*LATIN CAPITAL LETTER N WITH SMALL LETTER J*/
+  0x01CA, /*U+01CC*/ /*LATIN SMALL LETTER NJ*/
+  0x01CD, /*U+01CD*/ /**/
+  0x01CD, /*U+01CE*/ /*LATIN SMALL LETTER A WITH CARON*/
+  0x01CF, /*U+01CF*/ /**/
+  0x01CF, /*U+01D0*/ /*LATIN SMALL LETTER I WITH CARON*/
+  0x01D1, /*U+01D1*/ /**/
+  0x01D1, /*U+01D2*/ /*LATIN SMALL LETTER O WITH CARON*/
+  0x01D3, /*U+01D3*/ /**/
+  0x01D3, /*U+01D4*/ /*LATIN SMALL LETTER U WITH CARON*/
+  0x01D5, /*U+01D5*/ /**/
+  0x01D5, /*U+01D6*/ /*LATIN SMALL LETTER U WITH DIAERESIS AND MACRON*/
+  0x01D7, /*U+01D7*/ /**/
+  0x01D7, /*U+01D8*/ /*LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE*/
+  0x01D9, /*U+01D9*/ /**/
+  0x01D9, /*U+01DA*/ /*LATIN SMALL LETTER U WITH DIAERESIS AND CARON*/
+  0x01DB, /*U+01DB*/ /**/
+  0x01DB, /*U+01DC*/ /*LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE*/
+  0x018E, /*U+01DD*/ /*LATIN SMALL LETTER TURNED E*/
+  0x01DE, /*U+01DE*/ /**/
+  0x01DE, /*U+01DF*/ /*LATIN SMALL LETTER A WITH DIAERESIS AND MACRON*/
+  0x01E0, /*U+01E0*/ /**/
+  0x01E0, /*U+01E1*/ /*LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON*/
+  0x01E2, /*U+01E2*/ /**/
+  0x01E2, /*U+01E3*/ /*LATIN SMALL LETTER AE WITH MACRON*/
+  0x01E4, /*U+01E4*/ /**/
+  0x01E4, /*U+01E5*/ /*LATIN SMALL LETTER G WITH STROKE*/
+  0x01E6, /*U+01E6*/ /**/
+  0x01E6, /*U+01E7*/ /*LATIN SMALL LETTER G WITH CARON*/
+  0x01E8, /*U+01E8*/ /**/
+  0x01E8, /*U+01E9*/ /*LATIN SMALL LETTER K WITH CARON*/
+  0x01EA, /*U+01EA*/ /**/
+  0x01EA, /*U+01EB*/ /*LATIN SMALL LETTER O WITH OGONEK*/
+  0x01EC, /*U+01EC*/ /**/
+  0x01EC, /*U+01ED*/ /*LATIN SMALL LETTER O WITH OGONEK AND MACRON*/
+  0x01EE, /*U+01EE*/ /**/
+  0x01EE, /*U+01EF*/ /*LATIN SMALL LETTER EZH WITH CARON*/
+  0x01F0, /*U+01F0*/ /**/
+  0x01F1, /*U+01F1*/ /**/
+  0x01F1, /*U+01F2*/ /*LATIN CAPITAL LETTER D WITH SMALL LETTER Z*/
+  0x01F1, /*U+01F3*/ /*LATIN SMALL LETTER DZ*/
+  0x01F4, /*U+01F4*/ /**/
+  0x01F4, /*U+01F5*/ /*LATIN SMALL LETTER G WITH ACUTE*/
+  0x01F6, /*U+01F6*/ /**/
+  0x01F7, /*U+01F7*/ /**/
+  0x01F8, /*U+01F8*/ /**/
+  0x01F8, /*U+01F9*/ /*LATIN SMALL LETTER N WITH GRAVE*/
+  0x01FA, /*U+01FA*/ /**/
+  0x01FA, /*U+01FB*/ /*LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE*/
+  0x01FC, /*U+01FC*/ /**/
+  0x01FC, /*U+01FD*/ /*LATIN SMALL LETTER AE WITH ACUTE*/
+  0x01FE, /*U+01FE*/ /**/
+  0x01FE, /*U+01FF*/ /*LATIN SMALL LETTER O WITH STROKE AND ACUTE*/
+  0x0200, /*U+0200*/ /**/
+  0x0200, /*U+0201*/ /*LATIN SMALL LETTER A WITH DOUBLE GRAVE*/
+  0x0202, /*U+0202*/ /**/
+  0x0202, /*U+0203*/ /*LATIN SMALL LETTER A WITH INVERTED BREVE*/
+  0x0204, /*U+0204*/ /**/
+  0x0204, /*U+0205*/ /*LATIN SMALL LETTER E WITH DOUBLE GRAVE*/
+  0x0206, /*U+0206*/ /**/
+  0x0206, /*U+0207*/ /*LATIN SMALL LETTER E WITH INVERTED BREVE*/
+  0x0208, /*U+0208*/ /**/
+  0x0208, /*U+0209*/ /*LATIN SMALL LETTER I WITH DOUBLE GRAVE*/
+  0x020A, /*U+020A*/ /**/
+  0x020A, /*U+020B*/ /*LATIN SMALL LETTER I WITH INVERTED BREVE*/
+  0x020C, /*U+020C*/ /**/
+  0x020C, /*U+020D*/ /*LATIN SMALL LETTER O WITH DOUBLE GRAVE*/
+  0x020E, /*U+020E*/ /**/
+  0x020E, /*U+020F*/ /*LATIN SMALL LETTER O WITH INVERTED BREVE*/
+  0x0210, /*U+0210*/ /**/
+  0x0210, /*U+0211*/ /*LATIN SMALL LETTER R WITH DOUBLE GRAVE*/
+  0x0212, /*U+0212*/ /**/
+  0x0212, /*U+0213*/ /*LATIN SMALL LETTER R WITH INVERTED BREVE*/
+  0x0214, /*U+0214*/ /**/
+  0x0214, /*U+0215*/ /*LATIN SMALL LETTER U WITH DOUBLE GRAVE*/
+  0x0216, /*U+0216*/ /**/
+  0x0216, /*U+0217*/ /*LATIN SMALL LETTER U WITH INVERTED BREVE*/
+  0x0218, /*U+0218*/ /**/
+  0x0218, /*U+0219*/ /*LATIN SMALL LETTER S WITH COMMA BELOW*/
+  0x021A, /*U+021A*/ /**/
+  0x021A, /*U+021B*/ /*LATIN SMALL LETTER T WITH COMMA BELOW*/
+  0x021C, /*U+021C*/ /**/
+  0x021C, /*U+021D*/ /*LATIN SMALL LETTER YOGH*/
+  0x021E, /*U+021E*/ /**/
+  0x021E, /*U+021F*/ /*LATIN SMALL LETTER H WITH CARON*/
+  0x0220, /*U+0220*/ /**/
+  0x0221, /*U+0221*/ /**/
+  0x0222, /*U+0222*/ /**/
+  0x0222, /*U+0223*/ /*LATIN SMALL LETTER OU*/
+  0x0224, /*U+0224*/ /**/
+  0x0224, /*U+0225*/ /*LATIN SMALL LETTER Z WITH HOOK*/
+  0x0226, /*U+0226*/ /**/
+  0x0226, /*U+0227*/ /*LATIN SMALL LETTER A WITH DOT ABOVE*/
+  0x0228, /*U+0228*/ /**/
+  0x0228, /*U+0229*/ /*LATIN SMALL LETTER E WITH CEDILLA*/
+  0x022A, /*U+022A*/ /**/
+  0x022A, /*U+022B*/ /*LATIN SMALL LETTER O WITH DIAERESIS AND MACRON*/
+  0x022C, /*U+022C*/ /**/
+  0x022C, /*U+022D*/ /*LATIN SMALL LETTER O WITH TILDE AND MACRON*/
+  0x022E, /*U+022E*/ /**/
+  0x022E, /*U+022F*/ /*LATIN SMALL LETTER O WITH DOT ABOVE*/
+  0x0230, /*U+0230*/ /**/
+  0x0230, /*U+0231*/ /*LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON*/
+  0x0232, /*U+0232*/ /**/
+  0x0232, /*U+0233*/ /*LATIN SMALL LETTER Y WITH MACRON*/
+  0x0234, /*U+0234*/ /**/
+  0x0235, /*U+0235*/ /**/
+  0x0236, /*U+0236*/ /**/
+  0x0237, /*U+0237*/ /**/
+  0x0238, /*U+0238*/ /**/
+  0x0239, /*U+0239*/ /**/
+  0x023A, /*U+023A*/ /**/
+  0x023B, /*U+023B*/ /**/
+  0x023B, /*U+023C*/ /*LATIN SMALL LETTER C WITH STROKE*/
+  0x023D, /*U+023D*/ /**/
+  0x023E, /*U+023E*/ /**/
+  0x2C7E, /*U+023F*/ /*LATIN SMALL LETTER S WITH SWASH TAIL*/
+  0x2C7F, /*U+0240*/ /*LATIN SMALL LETTER Z WITH SWASH TAIL*/
+  0x0241, /*U+0241*/ /**/
+  0x0241, /*U+0242*/ /*LATIN SMALL LETTER GLOTTAL STOP*/
+  0x0243, /*U+0243*/ /**/
+  0x0244, /*U+0244*/ /**/
+  0x0245, /*U+0245*/ /**/
+  0x0246, /*U+0246*/ /**/
+  0x0246, /*U+0247*/ /*LATIN SMALL LETTER E WITH STROKE*/
+  0x0248, /*U+0248*/ /**/
+  0x0248, /*U+0249*/ /*LATIN SMALL LETTER J WITH STROKE*/
+  0x024A, /*U+024A*/ /**/
+  0x024A, /*U+024B*/ /*LATIN SMALL LETTER Q WITH HOOK TAIL*/
+  0x024C, /*U+024C*/ /**/
+  0x024C, /*U+024D*/ /*LATIN SMALL LETTER R WITH STROKE*/
+  0x024E, /*U+024E*/ /**/
+  0x024E, /*U+024F*/ /*LATIN SMALL LETTER Y WITH STROKE*/
+  0x2C6F, /*U+0250*/ /*LATIN SMALL LETTER TURNED A*/
+  0x2C6D, /*U+0251*/ /*LATIN SMALL LETTER ALPHA*/
+  0x2C70, /*U+0252*/ /*LATIN SMALL LETTER TURNED ALPHA*/
+  0x0181, /*U+0253*/ /*LATIN SMALL LETTER B WITH HOOK*/
+  0x0186, /*U+0254*/ /*LATIN SMALL LETTER OPEN O*/
+  0x0255, /*U+0255*/ /**/
+  0x0189, /*U+0256*/ /*LATIN SMALL LETTER D WITH TAIL*/
+  0x018A, /*U+0257*/ /*LATIN SMALL LETTER D WITH HOOK*/
+  0x0258, /*U+0258*/ /**/
+  0x018F, /*U+0259*/ /*LATIN SMALL LETTER SCHWA*/
+  0x025A, /*U+025A*/ /**/
+  0x0190, /*U+025B*/ /*LATIN SMALL LETTER OPEN E*/
+  0x025C, /*U+025C*/ /**/
+  0x025D, /*U+025D*/ /**/
+  0x025E, /*U+025E*/ /**/
+  0x025F, /*U+025F*/ /**/
+  0x0193, /*U+0260*/ /*LATIN SMALL LETTER G WITH HOOK*/
+  0x0261, /*U+0261*/ /**/
+  0x0262, /*U+0262*/ /**/
+  0x0194, /*U+0263*/ /*LATIN SMALL LETTER GAMMA*/
+  0x0264, /*U+0264*/ /**/
+  0xA78D, /*U+0265*/ /*LATIN SMALL LETTER TURNED H*/
+  0x0266, /*U+0266*/ /**/
+  0x0267, /*U+0267*/ /**/
+  0x0197, /*U+0268*/ /*LATIN SMALL LETTER I WITH STROKE*/
+  0x0196, /*U+0269*/ /*LATIN SMALL LETTER IOTA*/
+  0x026A, /*U+026A*/ /**/
+  0x2C62, /*U+026B*/ /*LATIN SMALL LETTER L WITH MIDDLE TILDE*/
+  0x026C, /*U+026C*/ /**/
+  0x026D, /*U+026D*/ /**/
+  0x026E, /*U+026E*/ /**/
+  0x019C, /*U+026F*/ /*LATIN SMALL LETTER TURNED M*/
+  0x0270, /*U+0270*/ /**/
+  0x2C6E, /*U+0271*/ /*LATIN SMALL LETTER M WITH HOOK*/
+  0x019D, /*U+0272*/ /*LATIN SMALL LETTER N WITH LEFT HOOK*/
+  0x0273, /*U+0273*/ /**/
+  0x0274, /*U+0274*/ /**/
+  0x019F, /*U+0275*/ /*LATIN SMALL LETTER BARRED O*/
+  0x0276, /*U+0276*/ /**/
+  0x0277, /*U+0277*/ /**/
+  0x0278, /*U+0278*/ /**/
+  0x0279, /*U+0279*/ /**/
+  0x027A, /*U+027A*/ /**/
+  0x027B, /*U+027B*/ /**/
+  0x027C, /*U+027C*/ /**/
+  0x2C64, /*U+027D*/ /*LATIN SMALL LETTER R WITH TAIL*/
+  0x027E, /*U+027E*/ /**/
+  0x027F, /*U+027F*/ /**/
+  0x01A6, /*U+0280*/ /*LATIN LETTER SMALL CAPITAL R*/
+  0x0281, /*U+0281*/ /**/
+  0x0282, /*U+0282*/ /**/
+  0x01A9, /*U+0283*/ /*LATIN SMALL LETTER ESH*/
+  0x0284, /*U+0284*/ /**/
+  0x0285, /*U+0285*/ /**/
+  0x0286, /*U+0286*/ /**/
+  0x0287, /*U+0287*/ /**/
+  0x01AE, /*U+0288*/ /*LATIN SMALL LETTER T WITH RETROFLEX HOOK*/
+  0x0244, /*U+0289*/ /*LATIN SMALL LETTER U BAR*/
+  0x01B1, /*U+028A*/ /*LATIN SMALL LETTER UPSILON*/
+  0x01B2, /*U+028B*/ /*LATIN SMALL LETTER V WITH HOOK*/
+  0x0245, /*U+028C*/ /*LATIN SMALL LETTER TURNED V*/
+  0x028D, /*U+028D*/ /**/
+  0x028E, /*U+028E*/ /**/
+  0x028F, /*U+028F*/ /**/
+  0x0290, /*U+0290*/ /**/
+  0x0291, /*U+0291*/ /**/
+  0x01B7, /*U+0292*/ /*LATIN SMALL LETTER EZH*/
+  0x0293, /*U+0293*/ /**/
+  0x0294, /*U+0294*/ /**/
+  0x0295, /*U+0295*/ /**/
+  0x0296, /*U+0296*/ /**/
+  0x0297, /*U+0297*/ /**/
+  0x0298, /*U+0298*/ /**/
+  0x0299, /*U+0299*/ /**/
+  0x029A, /*U+029A*/ /**/
+  0x029B, /*U+029B*/ /**/
+  0x029C, /*U+029C*/ /**/
+  0x029D, /*U+029D*/ /**/
+  0x029E, /*U+029E*/ /**/
+  0x029F, /*U+029F*/ /**/
+  0x02A0, /*U+02A0*/ /**/
+  0x02A1, /*U+02A1*/ /**/
+  0x02A2, /*U+02A2*/ /**/
+  0x02A3, /*U+02A3*/ /**/
+  0x02A4, /*U+02A4*/ /**/
+  0x02A5, /*U+02A5*/ /**/
+  0x02A6, /*U+02A6*/ /**/
+  0x02A7, /*U+02A7*/ /**/
+  0x02A8, /*U+02A8*/ /**/
+  0x02A9, /*U+02A9*/ /**/
+  0x02AA, /*U+02AA*/ /**/
+  0x02AB, /*U+02AB*/ /**/
+  0x02AC, /*U+02AC*/ /**/
+  0x02AD, /*U+02AD*/ /**/
+  0x02AE, /*U+02AE*/ /**/
+  0x02AF, /*U+02AF*/ /**/
+  0x02B0, /*U+02B0*/ /**/
+  0x02B1, /*U+02B1*/ /**/
+  0x02B2, /*U+02B2*/ /**/
+  0x02B3, /*U+02B3*/ /**/
+  0x02B4, /*U+02B4*/ /**/
+  0x02B5, /*U+02B5*/ /**/
+  0x02B6, /*U+02B6*/ /**/
+  0x02B7, /*U+02B7*/ /**/
+  0x02B8, /*U+02B8*/ /**/
+  0x02B9, /*U+02B9*/ /**/
+  0x02BA, /*U+02BA*/ /**/
+  0x02BB, /*U+02BB*/ /**/
+  0x02BC, /*U+02BC*/ /**/
+  0x02BD, /*U+02BD*/ /**/
+  0x02BE, /*U+02BE*/ /**/
+  0x02BF, /*U+02BF*/ /**/
+};
+
+static const u_int16_t upper_table_2[640] = {
+  0x0340, /*U+0340*/ /**/
+  0x0341, /*U+0341*/ /**/
+  0x0342, /*U+0342*/ /**/
+  0x0343, /*U+0343*/ /**/
+  0x0344, /*U+0344*/ /**/
+  0x0399, /*U+0345*/ /*COMBINING GREEK YPOGEGRAMMENI*/
+  0x0346, /*U+0346*/ /**/
+  0x0347, /*U+0347*/ /**/
+  0x0348, /*U+0348*/ /**/
+  0x0349, /*U+0349*/ /**/
+  0x034A, /*U+034A*/ /**/
+  0x034B, /*U+034B*/ /**/
+  0x034C, /*U+034C*/ /**/
+  0x034D, /*U+034D*/ /**/
+  0x034E, /*U+034E*/ /**/
+  0x034F, /*U+034F*/ /**/
+  0x0350, /*U+0350*/ /**/
+  0x0351, /*U+0351*/ /**/
+  0x0352, /*U+0352*/ /**/
+  0x0353, /*U+0353*/ /**/
+  0x0354, /*U+0354*/ /**/
+  0x0355, /*U+0355*/ /**/
+  0x0356, /*U+0356*/ /**/
+  0x0357, /*U+0357*/ /**/
+  0x0358, /*U+0358*/ /**/
+  0x0359, /*U+0359*/ /**/
+  0x035A, /*U+035A*/ /**/
+  0x035B, /*U+035B*/ /**/
+  0x035C, /*U+035C*/ /**/
+  0x035D, /*U+035D*/ /**/
+  0x035E, /*U+035E*/ /**/
+  0x035F, /*U+035F*/ /**/
+  0x0360, /*U+0360*/ /**/
+  0x0361, /*U+0361*/ /**/
+  0x0362, /*U+0362*/ /**/
+  0x0363, /*U+0363*/ /**/
+  0x0364, /*U+0364*/ /**/
+  0x0365, /*U+0365*/ /**/
+  0x0366, /*U+0366*/ /**/
+  0x0367, /*U+0367*/ /**/
+  0x0368, /*U+0368*/ /**/
+  0x0369, /*U+0369*/ /**/
+  0x036A, /*U+036A*/ /**/
+  0x036B, /*U+036B*/ /**/
+  0x036C, /*U+036C*/ /**/
+  0x036D, /*U+036D*/ /**/
+  0x036E, /*U+036E*/ /**/
+  0x036F, /*U+036F*/ /**/
+  0x0370, /*U+0370*/ /**/
+  0x0370, /*U+0371*/ /*GREEK SMALL LETTER HETA*/
+  0x0372, /*U+0372*/ /**/
+  0x0372, /*U+0373*/ /*GREEK SMALL LETTER ARCHAIC SAMPI*/
+  0x0374, /*U+0374*/ /**/
+  0x0375, /*U+0375*/ /**/
+  0x0376, /*U+0376*/ /**/
+  0x0376, /*U+0377*/ /*GREEK SMALL LETTER PAMPHYLIAN DIGAMMA*/
+  0x0378, /*U+0378*/ /**/
+  0x0379, /*U+0379*/ /**/
+  0x037A, /*U+037A*/ /**/
+  0x03FD, /*U+037B*/ /*GREEK SMALL REVERSED LUNATE SIGMA SYMBOL*/
+  0x03FE, /*U+037C*/ /*GREEK SMALL DOTTED LUNATE SIGMA SYMBOL*/
+  0x03FF, /*U+037D*/ /*GREEK SMALL REVERSED DOTTED LUNATE SIGMA SYMBOL*/
+  0x037E, /*U+037E*/ /**/
+  0x037F, /*U+037F*/ /**/
+  0x0380, /*U+0380*/ /**/
+  0x0381, /*U+0381*/ /**/
+  0x0382, /*U+0382*/ /**/
+  0x0383, /*U+0383*/ /**/
+  0x0384, /*U+0384*/ /**/
+  0x0385, /*U+0385*/ /**/
+  0x0386, /*U+0386*/ /**/
+  0x0387, /*U+0387*/ /**/
+  0x0388, /*U+0388*/ /**/
+  0x0389, /*U+0389*/ /**/
+  0x038A, /*U+038A*/ /**/
+  0x038B, /*U+038B*/ /**/
+  0x038C, /*U+038C*/ /**/
+  0x038D, /*U+038D*/ /**/
+  0x038E, /*U+038E*/ /**/
+  0x038F, /*U+038F*/ /**/
+  0x0390, /*U+0390*/ /**/
+  0x0391, /*U+0391*/ /**/
+  0x0392, /*U+0392*/ /**/
+  0x0393, /*U+0393*/ /**/
+  0x0394, /*U+0394*/ /**/
+  0x0395, /*U+0395*/ /**/
+  0x0396, /*U+0396*/ /**/
+  0x0397, /*U+0397*/ /**/
+  0x0398, /*U+0398*/ /**/
+  0x0399, /*U+0399*/ /**/
+  0x039A, /*U+039A*/ /**/
+  0x039B, /*U+039B*/ /**/
+  0x039C, /*U+039C*/ /**/
+  0x039D, /*U+039D*/ /**/
+  0x039E, /*U+039E*/ /**/
+  0x039F, /*U+039F*/ /**/
+  0x03A0, /*U+03A0*/ /**/
+  0x03A1, /*U+03A1*/ /**/
+  0x03A2, /*U+03A2*/ /**/
+  0x03A3, /*U+03A3*/ /**/
+  0x03A4, /*U+03A4*/ /**/
+  0x03A5, /*U+03A5*/ /**/
+  0x03A6, /*U+03A6*/ /**/
+  0x03A7, /*U+03A7*/ /**/
+  0x03A8, /*U+03A8*/ /**/
+  0x03A9, /*U+03A9*/ /**/
+  0x03AA, /*U+03AA*/ /**/
+  0x03AB, /*U+03AB*/ /**/
+  0x0386, /*U+03AC*/ /*GREEK SMALL LETTER ALPHA WITH TONOS*/
+  0x0388, /*U+03AD*/ /*GREEK SMALL LETTER EPSILON WITH TONOS*/
+  0x0389, /*U+03AE*/ /*GREEK SMALL LETTER ETA WITH TONOS*/
+  0x038A, /*U+03AF*/ /*GREEK SMALL LETTER IOTA WITH TONOS*/
+  0x03B0, /*U+03B0*/ /**/
+  0x0391, /*U+03B1*/ /*GREEK SMALL LETTER ALPHA*/
+  0x0392, /*U+03B2*/ /*GREEK SMALL LETTER BETA*/
+  0x0393, /*U+03B3*/ /*GREEK SMALL LETTER GAMMA*/
+  0x0394, /*U+03B4*/ /*GREEK SMALL LETTER DELTA*/
+  0x0395, /*U+03B5*/ /*GREEK SMALL LETTER EPSILON*/
+  0x0396, /*U+03B6*/ /*GREEK SMALL LETTER ZETA*/
+  0x0397, /*U+03B7*/ /*GREEK SMALL LETTER ETA*/
+  0x0398, /*U+03B8*/ /*GREEK SMALL LETTER THETA*/
+  0x0399, /*U+03B9*/ /*GREEK SMALL LETTER IOTA*/
+  0x039A, /*U+03BA*/ /*GREEK SMALL LETTER KAPPA*/
+  0x039B, /*U+03BB*/ /*GREEK SMALL LETTER LAMDA*/
+  0x039C, /*U+03BC*/ /*GREEK SMALL LETTER MU*/
+  0x039D, /*U+03BD*/ /*GREEK SMALL LETTER NU*/
+  0x039E, /*U+03BE*/ /*GREEK SMALL LETTER XI*/
+  0x039F, /*U+03BF*/ /*GREEK SMALL LETTER OMICRON*/
+  0x03A0, /*U+03C0*/ /*GREEK SMALL LETTER PI*/
+  0x03A1, /*U+03C1*/ /*GREEK SMALL LETTER RHO*/
+  0x03A3, /*U+03C2*/ /*GREEK SMALL LETTER FINAL SIGMA*/
+  0x03A3, /*U+03C3*/ /*GREEK SMALL LETTER SIGMA*/
+  0x03A4, /*U+03C4*/ /*GREEK SMALL LETTER TAU*/
+  0x03A5, /*U+03C5*/ /*GREEK SMALL LETTER UPSILON*/
+  0x03A6, /*U+03C6*/ /*GREEK SMALL LETTER PHI*/
+  0x03A7, /*U+03C7*/ /*GREEK SMALL LETTER CHI*/
+  0x03A8, /*U+03C8*/ /*GREEK SMALL LETTER PSI*/
+  0x03A9, /*U+03C9*/ /*GREEK SMALL LETTER OMEGA*/
+  0x03AA, /*U+03CA*/ /*GREEK SMALL LETTER IOTA WITH DIALYTIKA*/
+  0x03AB, /*U+03CB*/ /*GREEK SMALL LETTER UPSILON WITH DIALYTIKA*/
+  0x038C, /*U+03CC*/ /*GREEK SMALL LETTER OMICRON WITH TONOS*/
+  0x038E, /*U+03CD*/ /*GREEK SMALL LETTER UPSILON WITH TONOS*/
+  0x038F, /*U+03CE*/ /*GREEK SMALL LETTER OMEGA WITH TONOS*/
+  0x03CF, /*U+03CF*/ /**/
+  0x0392, /*U+03D0*/ /*GREEK BETA SYMBOL*/
+  0x0398, /*U+03D1*/ /*GREEK THETA SYMBOL*/
+  0x03D2, /*U+03D2*/ /**/
+  0x03D3, /*U+03D3*/ /**/
+  0x03D4, /*U+03D4*/ /**/
+  0x03A6, /*U+03D5*/ /*GREEK PHI SYMBOL*/
+  0x03A0, /*U+03D6*/ /*GREEK PI SYMBOL*/
+  0x03CF, /*U+03D7*/ /*GREEK KAI SYMBOL*/
+  0x03D8, /*U+03D8*/ /**/
+  0x03D8, /*U+03D9*/ /*GREEK SMALL LETTER ARCHAIC KOPPA*/
+  0x03DA, /*U+03DA*/ /**/
+  0x03DA, /*U+03DB*/ /*GREEK SMALL LETTER STIGMA*/
+  0x03DC, /*U+03DC*/ /**/
+  0x03DC, /*U+03DD*/ /*GREEK SMALL LETTER DIGAMMA*/
+  0x03DE, /*U+03DE*/ /**/
+  0x03DE, /*U+03DF*/ /*GREEK SMALL LETTER KOPPA*/
+  0x03E0, /*U+03E0*/ /**/
+  0x03E0, /*U+03E1*/ /*GREEK SMALL LETTER SAMPI*/
+  0x03E2, /*U+03E2*/ /**/
+  0x03E2, /*U+03E3*/ /*COPTIC SMALL LETTER SHEI*/
+  0x03E4, /*U+03E4*/ /**/
+  0x03E4, /*U+03E5*/ /*COPTIC SMALL LETTER FEI*/
+  0x03E6, /*U+03E6*/ /**/
+  0x03E6, /*U+03E7*/ /*COPTIC SMALL LETTER KHEI*/
+  0x03E8, /*U+03E8*/ /**/
+  0x03E8, /*U+03E9*/ /*COPTIC SMALL LETTER HORI*/
+  0x03EA, /*U+03EA*/ /**/
+  0x03EA, /*U+03EB*/ /*COPTIC SMALL LETTER GANGIA*/
+  0x03EC, /*U+03EC*/ /**/
+  0x03EC, /*U+03ED*/ /*COPTIC SMALL LETTER SHIMA*/
+  0x03EE, /*U+03EE*/ /**/
+  0x03EE, /*U+03EF*/ /*COPTIC SMALL LETTER DEI*/
+  0x039A, /*U+03F0*/ /*GREEK KAPPA SYMBOL*/
+  0x03A1, /*U+03F1*/ /*GREEK RHO SYMBOL*/
+  0x03F9, /*U+03F2*/ /*GREEK LUNATE SIGMA SYMBOL*/
+  0x03F3, /*U+03F3*/ /**/
+  0x03F4, /*U+03F4*/ /**/
+  0x0395, /*U+03F5*/ /*GREEK LUNATE EPSILON SYMBOL*/
+  0x03F6, /*U+03F6*/ /**/
+  0x03F7, /*U+03F7*/ /**/
+  0x03F7, /*U+03F8*/ /*GREEK SMALL LETTER SHO*/
+  0x03F9, /*U+03F9*/ /**/
+  0x03FA, /*U+03FA*/ /**/
+  0x03FA, /*U+03FB*/ /*GREEK SMALL LETTER SAN*/
+  0x03FC, /*U+03FC*/ /**/
+  0x03FD, /*U+03FD*/ /**/
+  0x03FE, /*U+03FE*/ /**/
+  0x03FF, /*U+03FF*/ /**/
+  0x0400, /*U+0400*/ /**/
+  0x0401, /*U+0401*/ /**/
+  0x0402, /*U+0402*/ /**/
+  0x0403, /*U+0403*/ /**/
+  0x0404, /*U+0404*/ /**/
+  0x0405, /*U+0405*/ /**/
+  0x0406, /*U+0406*/ /**/
+  0x0407, /*U+0407*/ /**/
+  0x0408, /*U+0408*/ /**/
+  0x0409, /*U+0409*/ /**/
+  0x040A, /*U+040A*/ /**/
+  0x040B, /*U+040B*/ /**/
+  0x040C, /*U+040C*/ /**/
+  0x040D, /*U+040D*/ /**/
+  0x040E, /*U+040E*/ /**/
+  0x040F, /*U+040F*/ /**/
+  0x0410, /*U+0410*/ /**/
+  0x0411, /*U+0411*/ /**/
+  0x0412, /*U+0412*/ /**/
+  0x0413, /*U+0413*/ /**/
+  0x0414, /*U+0414*/ /**/
+  0x0415, /*U+0415*/ /**/
+  0x0416, /*U+0416*/ /**/
+  0x0417, /*U+0417*/ /**/
+  0x0418, /*U+0418*/ /**/
+  0x0419, /*U+0419*/ /**/
+  0x041A, /*U+041A*/ /**/
+  0x041B, /*U+041B*/ /**/
+  0x041C, /*U+041C*/ /**/
+  0x041D, /*U+041D*/ /**/
+  0x041E, /*U+041E*/ /**/
+  0x041F, /*U+041F*/ /**/
+  0x0420, /*U+0420*/ /**/
+  0x0421, /*U+0421*/ /**/
+  0x0422, /*U+0422*/ /**/
+  0x0423, /*U+0423*/ /**/
+  0x0424, /*U+0424*/ /**/
+  0x0425, /*U+0425*/ /**/
+  0x0426, /*U+0426*/ /**/
+  0x0427, /*U+0427*/ /**/
+  0x0428, /*U+0428*/ /**/
+  0x0429, /*U+0429*/ /**/
+  0x042A, /*U+042A*/ /**/
+  0x042B, /*U+042B*/ /**/
+  0x042C, /*U+042C*/ /**/
+  0x042D, /*U+042D*/ /**/
+  0x042E, /*U+042E*/ /**/
+  0x042F, /*U+042F*/ /**/
+  0x0410, /*U+0430*/ /*CYRILLIC SMALL LETTER A*/
+  0x0411, /*U+0431*/ /*CYRILLIC SMALL LETTER BE*/
+  0x0412, /*U+0432*/ /*CYRILLIC SMALL LETTER VE*/
+  0x0413, /*U+0433*/ /*CYRILLIC SMALL LETTER GHE*/
+  0x0414, /*U+0434*/ /*CYRILLIC SMALL LETTER DE*/
+  0x0415, /*U+0435*/ /*CYRILLIC SMALL LETTER IE*/
+  0x0416, /*U+0436*/ /*CYRILLIC SMALL LETTER ZHE*/
+  0x0417, /*U+0437*/ /*CYRILLIC SMALL LETTER ZE*/
+  0x0418, /*U+0438*/ /*CYRILLIC SMALL LETTER I*/
+  0x0419, /*U+0439*/ /*CYRILLIC SMALL LETTER SHORT I*/
+  0x041A, /*U+043A*/ /*CYRILLIC SMALL LETTER KA*/
+  0x041B, /*U+043B*/ /*CYRILLIC SMALL LETTER EL*/
+  0x041C, /*U+043C*/ /*CYRILLIC SMALL LETTER EM*/
+  0x041D, /*U+043D*/ /*CYRILLIC SMALL LETTER EN*/
+  0x041E, /*U+043E*/ /*CYRILLIC SMALL LETTER O*/
+  0x041F, /*U+043F*/ /*CYRILLIC SMALL LETTER PE*/
+  0x0420, /*U+0440*/ /*CYRILLIC SMALL LETTER ER*/
+  0x0421, /*U+0441*/ /*CYRILLIC SMALL LETTER ES*/
+  0x0422, /*U+0442*/ /*CYRILLIC SMALL LETTER TE*/
+  0x0423, /*U+0443*/ /*CYRILLIC SMALL LETTER U*/
+  0x0424, /*U+0444*/ /*CYRILLIC SMALL LETTER EF*/
+  0x0425, /*U+0445*/ /*CYRILLIC SMALL LETTER HA*/
+  0x0426, /*U+0446*/ /*CYRILLIC SMALL LETTER TSE*/
+  0x0427, /*U+0447*/ /*CYRILLIC SMALL LETTER CHE*/
+  0x0428, /*U+0448*/ /*CYRILLIC SMALL LETTER SHA*/
+  0x0429, /*U+0449*/ /*CYRILLIC SMALL LETTER SHCHA*/
+  0x042A, /*U+044A*/ /*CYRILLIC SMALL LETTER HARD SIGN*/
+  0x042B, /*U+044B*/ /*CYRILLIC SMALL LETTER YERU*/
+  0x042C, /*U+044C*/ /*CYRILLIC SMALL LETTER SOFT SIGN*/
+  0x042D, /*U+044D*/ /*CYRILLIC SMALL LETTER E*/
+  0x042E, /*U+044E*/ /*CYRILLIC SMALL LETTER YU*/
+  0x042F, /*U+044F*/ /*CYRILLIC SMALL LETTER YA*/
+  0x0400, /*U+0450*/ /*CYRILLIC SMALL LETTER IE WITH GRAVE*/
+  0x0401, /*U+0451*/ /*CYRILLIC SMALL LETTER IO*/
+  0x0402, /*U+0452*/ /*CYRILLIC SMALL LETTER DJE*/
+  0x0403, /*U+0453*/ /*CYRILLIC SMALL LETTER GJE*/
+  0x0404, /*U+0454*/ /*CYRILLIC SMALL LETTER UKRAINIAN IE*/
+  0x0405, /*U+0455*/ /*CYRILLIC SMALL LETTER DZE*/
+  0x0406, /*U+0456*/ /*CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I*/
+  0x0407, /*U+0457*/ /*CYRILLIC SMALL LETTER YI*/
+  0x0408, /*U+0458*/ /*CYRILLIC SMALL LETTER JE*/
+  0x0409, /*U+0459*/ /*CYRILLIC SMALL LETTER LJE*/
+  0x040A, /*U+045A*/ /*CYRILLIC SMALL LETTER NJE*/
+  0x040B, /*U+045B*/ /*CYRILLIC SMALL LETTER TSHE*/
+  0x040C, /*U+045C*/ /*CYRILLIC SMALL LETTER KJE*/
+  0x040D, /*U+045D*/ /*CYRILLIC SMALL LETTER I WITH GRAVE*/
+  0x040E, /*U+045E*/ /*CYRILLIC SMALL LETTER SHORT U*/
+  0x040F, /*U+045F*/ /*CYRILLIC SMALL LETTER DZHE*/
+  0x0460, /*U+0460*/ /**/
+  0x0460, /*U+0461*/ /*CYRILLIC SMALL LETTER OMEGA*/
+  0x0462, /*U+0462*/ /**/
+  0x0462, /*U+0463*/ /*CYRILLIC SMALL LETTER YAT*/
+  0x0464, /*U+0464*/ /**/
+  0x0464, /*U+0465*/ /*CYRILLIC SMALL LETTER IOTIFIED E*/
+  0x0466, /*U+0466*/ /**/
+  0x0466, /*U+0467*/ /*CYRILLIC SMALL LETTER LITTLE YUS*/
+  0x0468, /*U+0468*/ /**/
+  0x0468, /*U+0469*/ /*CYRILLIC SMALL LETTER IOTIFIED LITTLE YUS*/
+  0x046A, /*U+046A*/ /**/
+  0x046A, /*U+046B*/ /*CYRILLIC SMALL LETTER BIG YUS*/
+  0x046C, /*U+046C*/ /**/
+  0x046C, /*U+046D*/ /*CYRILLIC SMALL LETTER IOTIFIED BIG YUS*/
+  0x046E, /*U+046E*/ /**/
+  0x046E, /*U+046F*/ /*CYRILLIC SMALL LETTER KSI*/
+  0x0470, /*U+0470*/ /**/
+  0x0470, /*U+0471*/ /*CYRILLIC SMALL LETTER PSI*/
+  0x0472, /*U+0472*/ /**/
+  0x0472, /*U+0473*/ /*CYRILLIC SMALL LETTER FITA*/
+  0x0474, /*U+0474*/ /**/
+  0x0474, /*U+0475*/ /*CYRILLIC SMALL LETTER IZHITSA*/
+  0x0476, /*U+0476*/ /**/
+  0x0476, /*U+0477*/ /*CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT*/
+  0x0478, /*U+0478*/ /**/
+  0x0478, /*U+0479*/ /*CYRILLIC SMALL LETTER UK*/
+  0x047A, /*U+047A*/ /**/
+  0x047A, /*U+047B*/ /*CYRILLIC SMALL LETTER ROUND OMEGA*/
+  0x047C, /*U+047C*/ /**/
+  0x047C, /*U+047D*/ /*CYRILLIC SMALL LETTER OMEGA WITH TITLO*/
+  0x047E, /*U+047E*/ /**/
+  0x047E, /*U+047F*/ /*CYRILLIC SMALL LETTER OT*/
+  0x0480, /*U+0480*/ /**/
+  0x0480, /*U+0481*/ /*CYRILLIC SMALL LETTER KOPPA*/
+  0x0482, /*U+0482*/ /**/
+  0x0483, /*U+0483*/ /**/
+  0x0484, /*U+0484*/ /**/
+  0x0485, /*U+0485*/ /**/
+  0x0486, /*U+0486*/ /**/
+  0x0487, /*U+0487*/ /**/
+  0x0488, /*U+0488*/ /**/
+  0x0489, /*U+0489*/ /**/
+  0x048A, /*U+048A*/ /**/
+  0x048A, /*U+048B*/ /*CYRILLIC SMALL LETTER SHORT I WITH TAIL*/
+  0x048C, /*U+048C*/ /**/
+  0x048C, /*U+048D*/ /*CYRILLIC SMALL LETTER SEMISOFT SIGN*/
+  0x048E, /*U+048E*/ /**/
+  0x048E, /*U+048F*/ /*CYRILLIC SMALL LETTER ER WITH TICK*/
+  0x0490, /*U+0490*/ /**/
+  0x0490, /*U+0491*/ /*CYRILLIC SMALL LETTER GHE WITH UPTURN*/
+  0x0492, /*U+0492*/ /**/
+  0x0492, /*U+0493*/ /*CYRILLIC SMALL LETTER GHE WITH STROKE*/
+  0x0494, /*U+0494*/ /**/
+  0x0494, /*U+0495*/ /*CYRILLIC SMALL LETTER GHE WITH MIDDLE HOOK*/
+  0x0496, /*U+0496*/ /**/
+  0x0496, /*U+0497*/ /*CYRILLIC SMALL LETTER ZHE WITH DESCENDER*/
+  0x0498, /*U+0498*/ /**/
+  0x0498, /*U+0499*/ /*CYRILLIC SMALL LETTER ZE WITH DESCENDER*/
+  0x049A, /*U+049A*/ /**/
+  0x049A, /*U+049B*/ /*CYRILLIC SMALL LETTER KA WITH DESCENDER*/
+  0x049C, /*U+049C*/ /**/
+  0x049C, /*U+049D*/ /*CYRILLIC SMALL LETTER KA WITH VERTICAL STROKE*/
+  0x049E, /*U+049E*/ /**/
+  0x049E, /*U+049F*/ /*CYRILLIC SMALL LETTER KA WITH STROKE*/
+  0x04A0, /*U+04A0*/ /**/
+  0x04A0, /*U+04A1*/ /*CYRILLIC SMALL LETTER BASHKIR KA*/
+  0x04A2, /*U+04A2*/ /**/
+  0x04A2, /*U+04A3*/ /*CYRILLIC SMALL LETTER EN WITH DESCENDER*/
+  0x04A4, /*U+04A4*/ /**/
+  0x04A4, /*U+04A5*/ /*CYRILLIC SMALL LIGATURE EN GHE*/
+  0x04A6, /*U+04A6*/ /**/
+  0x04A6, /*U+04A7*/ /*CYRILLIC SMALL LETTER PE WITH MIDDLE HOOK*/
+  0x04A8, /*U+04A8*/ /**/
+  0x04A8, /*U+04A9*/ /*CYRILLIC SMALL LETTER ABKHASIAN HA*/
+  0x04AA, /*U+04AA*/ /**/
+  0x04AA, /*U+04AB*/ /*CYRILLIC SMALL LETTER ES WITH DESCENDER*/
+  0x04AC, /*U+04AC*/ /**/
+  0x04AC, /*U+04AD*/ /*CYRILLIC SMALL LETTER TE WITH DESCENDER*/
+  0x04AE, /*U+04AE*/ /**/
+  0x04AE, /*U+04AF*/ /*CYRILLIC SMALL LETTER STRAIGHT U*/
+  0x04B0, /*U+04B0*/ /**/
+  0x04B0, /*U+04B1*/ /*CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE*/
+  0x04B2, /*U+04B2*/ /**/
+  0x04B2, /*U+04B3*/ /*CYRILLIC SMALL LETTER HA WITH DESCENDER*/
+  0x04B4, /*U+04B4*/ /**/
+  0x04B4, /*U+04B5*/ /*CYRILLIC SMALL LIGATURE TE TSE*/
+  0x04B6, /*U+04B6*/ /**/
+  0x04B6, /*U+04B7*/ /*CYRILLIC SMALL LETTER CHE WITH DESCENDER*/
+  0x04B8, /*U+04B8*/ /**/
+  0x04B8, /*U+04B9*/ /*CYRILLIC SMALL LETTER CHE WITH VERTICAL STROKE*/
+  0x04BA, /*U+04BA*/ /**/
+  0x04BA, /*U+04BB*/ /*CYRILLIC SMALL LETTER SHHA*/
+  0x04BC, /*U+04BC*/ /**/
+  0x04BC, /*U+04BD*/ /*CYRILLIC SMALL LETTER ABKHASIAN CHE*/
+  0x04BE, /*U+04BE*/ /**/
+  0x04BE, /*U+04BF*/ /*CYRILLIC SMALL LETTER ABKHASIAN CHE WITH DESCENDER*/
+  0x04C0, /*U+04C0*/ /**/
+  0x04C1, /*U+04C1*/ /**/
+  0x04C1, /*U+04C2*/ /*CYRILLIC SMALL LETTER ZHE WITH BREVE*/
+  0x04C3, /*U+04C3*/ /**/
+  0x04C3, /*U+04C4*/ /*CYRILLIC SMALL LETTER KA WITH HOOK*/
+  0x04C5, /*U+04C5*/ /**/
+  0x04C5, /*U+04C6*/ /*CYRILLIC SMALL LETTER EL WITH TAIL*/
+  0x04C7, /*U+04C7*/ /**/
+  0x04C7, /*U+04C8*/ /*CYRILLIC SMALL LETTER EN WITH HOOK*/
+  0x04C9, /*U+04C9*/ /**/
+  0x04C9, /*U+04CA*/ /*CYRILLIC SMALL LETTER EN WITH TAIL*/
+  0x04CB, /*U+04CB*/ /**/
+  0x04CB, /*U+04CC*/ /*CYRILLIC SMALL LETTER KHAKASSIAN CHE*/
+  0x04CD, /*U+04CD*/ /**/
+  0x04CD, /*U+04CE*/ /*CYRILLIC SMALL LETTER EM WITH TAIL*/
+  0x04C0, /*U+04CF*/ /*CYRILLIC SMALL LETTER PALOCHKA*/
+  0x04D0, /*U+04D0*/ /**/
+  0x04D0, /*U+04D1*/ /*CYRILLIC SMALL LETTER A WITH BREVE*/
+  0x04D2, /*U+04D2*/ /**/
+  0x04D2, /*U+04D3*/ /*CYRILLIC SMALL LETTER A WITH DIAERESIS*/
+  0x04D4, /*U+04D4*/ /**/
+  0x04D4, /*U+04D5*/ /*CYRILLIC SMALL LIGATURE A IE*/
+  0x04D6, /*U+04D6*/ /**/
+  0x04D6, /*U+04D7*/ /*CYRILLIC SMALL LETTER IE WITH BREVE*/
+  0x04D8, /*U+04D8*/ /**/
+  0x04D8, /*U+04D9*/ /*CYRILLIC SMALL LETTER SCHWA*/
+  0x04DA, /*U+04DA*/ /**/
+  0x04DA, /*U+04DB*/ /*CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS*/
+  0x04DC, /*U+04DC*/ /**/
+  0x04DC, /*U+04DD*/ /*CYRILLIC SMALL LETTER ZHE WITH DIAERESIS*/
+  0x04DE, /*U+04DE*/ /**/
+  0x04DE, /*U+04DF*/ /*CYRILLIC SMALL LETTER ZE WITH DIAERESIS*/
+  0x04E0, /*U+04E0*/ /**/
+  0x04E0, /*U+04E1*/ /*CYRILLIC SMALL LETTER ABKHASIAN DZE*/
+  0x04E2, /*U+04E2*/ /**/
+  0x04E2, /*U+04E3*/ /*CYRILLIC SMALL LETTER I WITH MACRON*/
+  0x04E4, /*U+04E4*/ /**/
+  0x04E4, /*U+04E5*/ /*CYRILLIC SMALL LETTER I WITH DIAERESIS*/
+  0x04E6, /*U+04E6*/ /**/
+  0x04E6, /*U+04E7*/ /*CYRILLIC SMALL LETTER O WITH DIAERESIS*/
+  0x04E8, /*U+04E8*/ /**/
+  0x04E8, /*U+04E9*/ /*CYRILLIC SMALL LETTER BARRED O*/
+  0x04EA, /*U+04EA*/ /**/
+  0x04EA, /*U+04EB*/ /*CYRILLIC SMALL LETTER BARRED O WITH DIAERESIS*/
+  0x04EC, /*U+04EC*/ /**/
+  0x04EC, /*U+04ED*/ /*CYRILLIC SMALL LETTER E WITH DIAERESIS*/
+  0x04EE, /*U+04EE*/ /**/
+  0x04EE, /*U+04EF*/ /*CYRILLIC SMALL LETTER U WITH MACRON*/
+  0x04F0, /*U+04F0*/ /**/
+  0x04F0, /*U+04F1*/ /*CYRILLIC SMALL LETTER U WITH DIAERESIS*/
+  0x04F2, /*U+04F2*/ /**/
+  0x04F2, /*U+04F3*/ /*CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE*/
+  0x04F4, /*U+04F4*/ /**/
+  0x04F4, /*U+04F5*/ /*CYRILLIC SMALL LETTER CHE WITH DIAERESIS*/
+  0x04F6, /*U+04F6*/ /**/
+  0x04F6, /*U+04F7*/ /*CYRILLIC SMALL LETTER GHE WITH DESCENDER*/
+  0x04F8, /*U+04F8*/ /**/
+  0x04F8, /*U+04F9*/ /*CYRILLIC SMALL LETTER YERU WITH DIAERESIS*/
+  0x04FA, /*U+04FA*/ /**/
+  0x04FA, /*U+04FB*/ /*CYRILLIC SMALL LETTER GHE WITH STROKE AND HOOK*/
+  0x04FC, /*U+04FC*/ /**/
+  0x04FC, /*U+04FD*/ /*CYRILLIC SMALL LETTER HA WITH HOOK*/
+  0x04FE, /*U+04FE*/ /**/
+  0x04FE, /*U+04FF*/ /*CYRILLIC SMALL LETTER HA WITH STROKE*/
+  0x0500, /*U+0500*/ /**/
+  0x0500, /*U+0501*/ /*CYRILLIC SMALL LETTER KOMI DE*/
+  0x0502, /*U+0502*/ /**/
+  0x0502, /*U+0503*/ /*CYRILLIC SMALL LETTER KOMI DJE*/
+  0x0504, /*U+0504*/ /**/
+  0x0504, /*U+0505*/ /*CYRILLIC SMALL LETTER KOMI ZJE*/
+  0x0506, /*U+0506*/ /**/
+  0x0506, /*U+0507*/ /*CYRILLIC SMALL LETTER KOMI DZJE*/
+  0x0508, /*U+0508*/ /**/
+  0x0508, /*U+0509*/ /*CYRILLIC SMALL LETTER KOMI LJE*/
+  0x050A, /*U+050A*/ /**/
+  0x050A, /*U+050B*/ /*CYRILLIC SMALL LETTER KOMI NJE*/
+  0x050C, /*U+050C*/ /**/
+  0x050C, /*U+050D*/ /*CYRILLIC SMALL LETTER KOMI SJE*/
+  0x050E, /*U+050E*/ /**/
+  0x050E, /*U+050F*/ /*CYRILLIC SMALL LETTER KOMI TJE*/
+  0x0510, /*U+0510*/ /**/
+  0x0510, /*U+0511*/ /*CYRILLIC SMALL LETTER REVERSED ZE*/
+  0x0512, /*U+0512*/ /**/
+  0x0512, /*U+0513*/ /*CYRILLIC SMALL LETTER EL WITH HOOK*/
+  0x0514, /*U+0514*/ /**/
+  0x0514, /*U+0515*/ /*CYRILLIC SMALL LETTER LHA*/
+  0x0516, /*U+0516*/ /**/
+  0x0516, /*U+0517*/ /*CYRILLIC SMALL LETTER RHA*/
+  0x0518, /*U+0518*/ /**/
+  0x0518, /*U+0519*/ /*CYRILLIC SMALL LETTER YAE*/
+  0x051A, /*U+051A*/ /**/
+  0x051A, /*U+051B*/ /*CYRILLIC SMALL LETTER QA*/
+  0x051C, /*U+051C*/ /**/
+  0x051C, /*U+051D*/ /*CYRILLIC SMALL LETTER WE*/
+  0x051E, /*U+051E*/ /**/
+  0x051E, /*U+051F*/ /*CYRILLIC SMALL LETTER ALEUT KA*/
+  0x0520, /*U+0520*/ /**/
+  0x0520, /*U+0521*/ /*CYRILLIC SMALL LETTER EL WITH MIDDLE HOOK*/
+  0x0522, /*U+0522*/ /**/
+  0x0522, /*U+0523*/ /*CYRILLIC SMALL LETTER EN WITH MIDDLE HOOK*/
+  0x0524, /*U+0524*/ /**/
+  0x0524, /*U+0525*/ /*CYRILLIC SMALL LETTER PE WITH DESCENDER*/
+  0x0526, /*U+0526*/ /**/
+  0x0526, /*U+0527*/ /*CYRILLIC SMALL LETTER SHHA WITH DESCENDER*/
+  0x0528, /*U+0528*/ /**/
+  0x0529, /*U+0529*/ /**/
+  0x052A, /*U+052A*/ /**/
+  0x052B, /*U+052B*/ /**/
+  0x052C, /*U+052C*/ /**/
+  0x052D, /*U+052D*/ /**/
+  0x052E, /*U+052E*/ /**/
+  0x052F, /*U+052F*/ /**/
+  0x0530, /*U+0530*/ /**/
+  0x0531, /*U+0531*/ /**/
+  0x0532, /*U+0532*/ /**/
+  0x0533, /*U+0533*/ /**/
+  0x0534, /*U+0534*/ /**/
+  0x0535, /*U+0535*/ /**/
+  0x0536, /*U+0536*/ /**/
+  0x0537, /*U+0537*/ /**/
+  0x0538, /*U+0538*/ /**/
+  0x0539, /*U+0539*/ /**/
+  0x053A, /*U+053A*/ /**/
+  0x053B, /*U+053B*/ /**/
+  0x053C, /*U+053C*/ /**/
+  0x053D, /*U+053D*/ /**/
+  0x053E, /*U+053E*/ /**/
+  0x053F, /*U+053F*/ /**/
+  0x0540, /*U+0540*/ /**/
+  0x0541, /*U+0541*/ /**/
+  0x0542, /*U+0542*/ /**/
+  0x0543, /*U+0543*/ /**/
+  0x0544, /*U+0544*/ /**/
+  0x0545, /*U+0545*/ /**/
+  0x0546, /*U+0546*/ /**/
+  0x0547, /*U+0547*/ /**/
+  0x0548, /*U+0548*/ /**/
+  0x0549, /*U+0549*/ /**/
+  0x054A, /*U+054A*/ /**/
+  0x054B, /*U+054B*/ /**/
+  0x054C, /*U+054C*/ /**/
+  0x054D, /*U+054D*/ /**/
+  0x054E, /*U+054E*/ /**/
+  0x054F, /*U+054F*/ /**/
+  0x0550, /*U+0550*/ /**/
+  0x0551, /*U+0551*/ /**/
+  0x0552, /*U+0552*/ /**/
+  0x0553, /*U+0553*/ /**/
+  0x0554, /*U+0554*/ /**/
+  0x0555, /*U+0555*/ /**/
+  0x0556, /*U+0556*/ /**/
+  0x0557, /*U+0557*/ /**/
+  0x0558, /*U+0558*/ /**/
+  0x0559, /*U+0559*/ /**/
+  0x055A, /*U+055A*/ /**/
+  0x055B, /*U+055B*/ /**/
+  0x055C, /*U+055C*/ /**/
+  0x055D, /*U+055D*/ /**/
+  0x055E, /*U+055E*/ /**/
+  0x055F, /*U+055F*/ /**/
+  0x0560, /*U+0560*/ /**/
+  0x0531, /*U+0561*/ /*ARMENIAN SMALL LETTER AYB*/
+  0x0532, /*U+0562*/ /*ARMENIAN SMALL LETTER BEN*/
+  0x0533, /*U+0563*/ /*ARMENIAN SMALL LETTER GIM*/
+  0x0534, /*U+0564*/ /*ARMENIAN SMALL LETTER DA*/
+  0x0535, /*U+0565*/ /*ARMENIAN SMALL LETTER ECH*/
+  0x0536, /*U+0566*/ /*ARMENIAN SMALL LETTER ZA*/
+  0x0537, /*U+0567*/ /*ARMENIAN SMALL LETTER EH*/
+  0x0538, /*U+0568*/ /*ARMENIAN SMALL LETTER ET*/
+  0x0539, /*U+0569*/ /*ARMENIAN SMALL LETTER TO*/
+  0x053A, /*U+056A*/ /*ARMENIAN SMALL LETTER ZHE*/
+  0x053B, /*U+056B*/ /*ARMENIAN SMALL LETTER INI*/
+  0x053C, /*U+056C*/ /*ARMENIAN SMALL LETTER LIWN*/
+  0x053D, /*U+056D*/ /*ARMENIAN SMALL LETTER XEH*/
+  0x053E, /*U+056E*/ /*ARMENIAN SMALL LETTER CA*/
+  0x053F, /*U+056F*/ /*ARMENIAN SMALL LETTER KEN*/
+  0x0540, /*U+0570*/ /*ARMENIAN SMALL LETTER HO*/
+  0x0541, /*U+0571*/ /*ARMENIAN SMALL LETTER JA*/
+  0x0542, /*U+0572*/ /*ARMENIAN SMALL LETTER GHAD*/
+  0x0543, /*U+0573*/ /*ARMENIAN SMALL LETTER CHEH*/
+  0x0544, /*U+0574*/ /*ARMENIAN SMALL LETTER MEN*/
+  0x0545, /*U+0575*/ /*ARMENIAN SMALL LETTER YI*/
+  0x0546, /*U+0576*/ /*ARMENIAN SMALL LETTER NOW*/
+  0x0547, /*U+0577*/ /*ARMENIAN SMALL LETTER SHA*/
+  0x0548, /*U+0578*/ /*ARMENIAN SMALL LETTER VO*/
+  0x0549, /*U+0579*/ /*ARMENIAN SMALL LETTER CHA*/
+  0x054A, /*U+057A*/ /*ARMENIAN SMALL LETTER PEH*/
+  0x054B, /*U+057B*/ /*ARMENIAN SMALL LETTER JHEH*/
+  0x054C, /*U+057C*/ /*ARMENIAN SMALL LETTER RA*/
+  0x054D, /*U+057D*/ /*ARMENIAN SMALL LETTER SEH*/
+  0x054E, /*U+057E*/ /*ARMENIAN SMALL LETTER VEW*/
+  0x054F, /*U+057F*/ /*ARMENIAN SMALL LETTER TIWN*/
+  0x0550, /*U+0580*/ /*ARMENIAN SMALL LETTER REH*/
+  0x0551, /*U+0581*/ /*ARMENIAN SMALL LETTER CO*/
+  0x0552, /*U+0582*/ /*ARMENIAN SMALL LETTER YIWN*/
+  0x0553, /*U+0583*/ /*ARMENIAN SMALL LETTER PIWR*/
+  0x0554, /*U+0584*/ /*ARMENIAN SMALL LETTER KEH*/
+  0x0555, /*U+0585*/ /*ARMENIAN SMALL LETTER OH*/
+  0x0556, /*U+0586*/ /*ARMENIAN SMALL LETTER FEH*/
+  0x0587, /*U+0587*/ /**/
+  0x0588, /*U+0588*/ /**/
+  0x0589, /*U+0589*/ /**/
+  0x058A, /*U+058A*/ /**/
+  0x058B, /*U+058B*/ /**/
+  0x058C, /*U+058C*/ /**/
+  0x058D, /*U+058D*/ /**/
+  0x058E, /*U+058E*/ /**/
+  0x058F, /*U+058F*/ /**/
+  0x0590, /*U+0590*/ /**/
+  0x0591, /*U+0591*/ /**/
+  0x0592, /*U+0592*/ /**/
+  0x0593, /*U+0593*/ /**/
+  0x0594, /*U+0594*/ /**/
+  0x0595, /*U+0595*/ /**/
+  0x0596, /*U+0596*/ /**/
+  0x0597, /*U+0597*/ /**/
+  0x0598, /*U+0598*/ /**/
+  0x0599, /*U+0599*/ /**/
+  0x059A, /*U+059A*/ /**/
+  0x059B, /*U+059B*/ /**/
+  0x059C, /*U+059C*/ /**/
+  0x059D, /*U+059D*/ /**/
+  0x059E, /*U+059E*/ /**/
+  0x059F, /*U+059F*/ /**/
+  0x05A0, /*U+05A0*/ /**/
+  0x05A1, /*U+05A1*/ /**/
+  0x05A2, /*U+05A2*/ /**/
+  0x05A3, /*U+05A3*/ /**/
+  0x05A4, /*U+05A4*/ /**/
+  0x05A5, /*U+05A5*/ /**/
+  0x05A6, /*U+05A6*/ /**/
+  0x05A7, /*U+05A7*/ /**/
+  0x05A8, /*U+05A8*/ /**/
+  0x05A9, /*U+05A9*/ /**/
+  0x05AA, /*U+05AA*/ /**/
+  0x05AB, /*U+05AB*/ /**/
+  0x05AC, /*U+05AC*/ /**/
+  0x05AD, /*U+05AD*/ /**/
+  0x05AE, /*U+05AE*/ /**/
+  0x05AF, /*U+05AF*/ /**/
+  0x05B0, /*U+05B0*/ /**/
+  0x05B1, /*U+05B1*/ /**/
+  0x05B2, /*U+05B2*/ /**/
+  0x05B3, /*U+05B3*/ /**/
+  0x05B4, /*U+05B4*/ /**/
+  0x05B5, /*U+05B5*/ /**/
+  0x05B6, /*U+05B6*/ /**/
+  0x05B7, /*U+05B7*/ /**/
+  0x05B8, /*U+05B8*/ /**/
+  0x05B9, /*U+05B9*/ /**/
+  0x05BA, /*U+05BA*/ /**/
+  0x05BB, /*U+05BB*/ /**/
+  0x05BC, /*U+05BC*/ /**/
+  0x05BD, /*U+05BD*/ /**/
+  0x05BE, /*U+05BE*/ /**/
+  0x05BF, /*U+05BF*/ /**/
+};
+
+static const u_int16_t upper_table_3[64] = {
+  0x1D40, /*U+1D40*/ /**/
+  0x1D41, /*U+1D41*/ /**/
+  0x1D42, /*U+1D42*/ /**/
+  0x1D43, /*U+1D43*/ /**/
+  0x1D44, /*U+1D44*/ /**/
+  0x1D45, /*U+1D45*/ /**/
+  0x1D46, /*U+1D46*/ /**/
+  0x1D47, /*U+1D47*/ /**/
+  0x1D48, /*U+1D48*/ /**/
+  0x1D49, /*U+1D49*/ /**/
+  0x1D4A, /*U+1D4A*/ /**/
+  0x1D4B, /*U+1D4B*/ /**/
+  0x1D4C, /*U+1D4C*/ /**/
+  0x1D4D, /*U+1D4D*/ /**/
+  0x1D4E, /*U+1D4E*/ /**/
+  0x1D4F, /*U+1D4F*/ /**/
+  0x1D50, /*U+1D50*/ /**/
+  0x1D51, /*U+1D51*/ /**/
+  0x1D52, /*U+1D52*/ /**/
+  0x1D53, /*U+1D53*/ /**/
+  0x1D54, /*U+1D54*/ /**/
+  0x1D55, /*U+1D55*/ /**/
+  0x1D56, /*U+1D56*/ /**/
+  0x1D57, /*U+1D57*/ /**/
+  0x1D58, /*U+1D58*/ /**/
+  0x1D59, /*U+1D59*/ /**/
+  0x1D5A, /*U+1D5A*/ /**/
+  0x1D5B, /*U+1D5B*/ /**/
+  0x1D5C, /*U+1D5C*/ /**/
+  0x1D5D, /*U+1D5D*/ /**/
+  0x1D5E, /*U+1D5E*/ /**/
+  0x1D5F, /*U+1D5F*/ /**/
+  0x1D60, /*U+1D60*/ /**/
+  0x1D61, /*U+1D61*/ /**/
+  0x1D62, /*U+1D62*/ /**/
+  0x1D63, /*U+1D63*/ /**/
+  0x1D64, /*U+1D64*/ /**/
+  0x1D65, /*U+1D65*/ /**/
+  0x1D66, /*U+1D66*/ /**/
+  0x1D67, /*U+1D67*/ /**/
+  0x1D68, /*U+1D68*/ /**/
+  0x1D69, /*U+1D69*/ /**/
+  0x1D6A, /*U+1D6A*/ /**/
+  0x1D6B, /*U+1D6B*/ /**/
+  0x1D6C, /*U+1D6C*/ /**/
+  0x1D6D, /*U+1D6D*/ /**/
+  0x1D6E, /*U+1D6E*/ /**/
+  0x1D6F, /*U+1D6F*/ /**/
+  0x1D70, /*U+1D70*/ /**/
+  0x1D71, /*U+1D71*/ /**/
+  0x1D72, /*U+1D72*/ /**/
+  0x1D73, /*U+1D73*/ /**/
+  0x1D74, /*U+1D74*/ /**/
+  0x1D75, /*U+1D75*/ /**/
+  0x1D76, /*U+1D76*/ /**/
+  0x1D77, /*U+1D77*/ /**/
+  0x1D78, /*U+1D78*/ /**/
+  0xA77D, /*U+1D79*/ /*LATIN SMALL LETTER INSULAR G*/
+  0x1D7A, /*U+1D7A*/ /**/
+  0x1D7B, /*U+1D7B*/ /**/
+  0x1D7C, /*U+1D7C*/ /**/
+  0x2C63, /*U+1D7D*/ /*LATIN SMALL LETTER P WITH STROKE*/
+  0x1D7E, /*U+1D7E*/ /**/
+  0x1D7F, /*U+1D7F*/ /**/
+};
+
+static const u_int16_t upper_table_4[512] = {
+  0x1E00, /*U+1E00*/ /**/
+  0x1E00, /*U+1E01*/ /*LATIN SMALL LETTER A WITH RING BELOW*/
+  0x1E02, /*U+1E02*/ /**/
+  0x1E02, /*U+1E03*/ /*LATIN SMALL LETTER B WITH DOT ABOVE*/
+  0x1E04, /*U+1E04*/ /**/
+  0x1E04, /*U+1E05*/ /*LATIN SMALL LETTER B WITH DOT BELOW*/
+  0x1E06, /*U+1E06*/ /**/
+  0x1E06, /*U+1E07*/ /*LATIN SMALL LETTER B WITH LINE BELOW*/
+  0x1E08, /*U+1E08*/ /**/
+  0x1E08, /*U+1E09*/ /*LATIN SMALL LETTER C WITH CEDILLA AND ACUTE*/
+  0x1E0A, /*U+1E0A*/ /**/
+  0x1E0A, /*U+1E0B*/ /*LATIN SMALL LETTER D WITH DOT ABOVE*/
+  0x1E0C, /*U+1E0C*/ /**/
+  0x1E0C, /*U+1E0D*/ /*LATIN SMALL LETTER D WITH DOT BELOW*/
+  0x1E0E, /*U+1E0E*/ /**/
+  0x1E0E, /*U+1E0F*/ /*LATIN SMALL LETTER D WITH LINE BELOW*/
+  0x1E10, /*U+1E10*/ /**/
+  0x1E10, /*U+1E11*/ /*LATIN SMALL LETTER D WITH CEDILLA*/
+  0x1E12, /*U+1E12*/ /**/
+  0x1E12, /*U+1E13*/ /*LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW*/
+  0x1E14, /*U+1E14*/ /**/
+  0x1E14, /*U+1E15*/ /*LATIN SMALL LETTER E WITH MACRON AND GRAVE*/
+  0x1E16, /*U+1E16*/ /**/
+  0x1E16, /*U+1E17*/ /*LATIN SMALL LETTER E WITH MACRON AND ACUTE*/
+  0x1E18, /*U+1E18*/ /**/
+  0x1E18, /*U+1E19*/ /*LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW*/
+  0x1E1A, /*U+1E1A*/ /**/
+  0x1E1A, /*U+1E1B*/ /*LATIN SMALL LETTER E WITH TILDE BELOW*/
+  0x1E1C, /*U+1E1C*/ /**/
+  0x1E1C, /*U+1E1D*/ /*LATIN SMALL LETTER E WITH CEDILLA AND BREVE*/
+  0x1E1E, /*U+1E1E*/ /**/
+  0x1E1E, /*U+1E1F*/ /*LATIN SMALL LETTER F WITH DOT ABOVE*/
+  0x1E20, /*U+1E20*/ /**/
+  0x1E20, /*U+1E21*/ /*LATIN SMALL LETTER G WITH MACRON*/
+  0x1E22, /*U+1E22*/ /**/
+  0x1E22, /*U+1E23*/ /*LATIN SMALL LETTER H WITH DOT ABOVE*/
+  0x1E24, /*U+1E24*/ /**/
+  0x1E24, /*U+1E25*/ /*LATIN SMALL LETTER H WITH DOT BELOW*/
+  0x1E26, /*U+1E26*/ /**/
+  0x1E26, /*U+1E27*/ /*LATIN SMALL LETTER H WITH DIAERESIS*/
+  0x1E28, /*U+1E28*/ /**/
+  0x1E28, /*U+1E29*/ /*LATIN SMALL LETTER H WITH CEDILLA*/
+  0x1E2A, /*U+1E2A*/ /**/
+  0x1E2A, /*U+1E2B*/ /*LATIN SMALL LETTER H WITH BREVE BELOW*/
+  0x1E2C, /*U+1E2C*/ /**/
+  0x1E2C, /*U+1E2D*/ /*LATIN SMALL LETTER I WITH TILDE BELOW*/
+  0x1E2E, /*U+1E2E*/ /**/
+  0x1E2E, /*U+1E2F*/ /*LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE*/
+  0x1E30, /*U+1E30*/ /**/
+  0x1E30, /*U+1E31*/ /*LATIN SMALL LETTER K WITH ACUTE*/
+  0x1E32, /*U+1E32*/ /**/
+  0x1E32, /*U+1E33*/ /*LATIN SMALL LETTER K WITH DOT BELOW*/
+  0x1E34, /*U+1E34*/ /**/
+  0x1E34, /*U+1E35*/ /*LATIN SMALL LETTER K WITH LINE BELOW*/
+  0x1E36, /*U+1E36*/ /**/
+  0x1E36, /*U+1E37*/ /*LATIN SMALL LETTER L WITH DOT BELOW*/
+  0x1E38, /*U+1E38*/ /**/
+  0x1E38, /*U+1E39*/ /*LATIN SMALL LETTER L WITH DOT BELOW AND MACRON*/
+  0x1E3A, /*U+1E3A*/ /**/
+  0x1E3A, /*U+1E3B*/ /*LATIN SMALL LETTER L WITH LINE BELOW*/
+  0x1E3C, /*U+1E3C*/ /**/
+  0x1E3C, /*U+1E3D*/ /*LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW*/
+  0x1E3E, /*U+1E3E*/ /**/
+  0x1E3E, /*U+1E3F*/ /*LATIN SMALL LETTER M WITH ACUTE*/
+  0x1E40, /*U+1E40*/ /**/
+  0x1E40, /*U+1E41*/ /*LATIN SMALL LETTER M WITH DOT ABOVE*/
+  0x1E42, /*U+1E42*/ /**/
+  0x1E42, /*U+1E43*/ /*LATIN SMALL LETTER M WITH DOT BELOW*/
+  0x1E44, /*U+1E44*/ /**/
+  0x1E44, /*U+1E45*/ /*LATIN SMALL LETTER N WITH DOT ABOVE*/
+  0x1E46, /*U+1E46*/ /**/
+  0x1E46, /*U+1E47*/ /*LATIN SMALL LETTER N WITH DOT BELOW*/
+  0x1E48, /*U+1E48*/ /**/
+  0x1E48, /*U+1E49*/ /*LATIN SMALL LETTER N WITH LINE BELOW*/
+  0x1E4A, /*U+1E4A*/ /**/
+  0x1E4A, /*U+1E4B*/ /*LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW*/
+  0x1E4C, /*U+1E4C*/ /**/
+  0x1E4C, /*U+1E4D*/ /*LATIN SMALL LETTER O WITH TILDE AND ACUTE*/
+  0x1E4E, /*U+1E4E*/ /**/
+  0x1E4E, /*U+1E4F*/ /*LATIN SMALL LETTER O WITH TILDE AND DIAERESIS*/
+  0x1E50, /*U+1E50*/ /**/
+  0x1E50, /*U+1E51*/ /*LATIN SMALL LETTER O WITH MACRON AND GRAVE*/
+  0x1E52, /*U+1E52*/ /**/
+  0x1E52, /*U+1E53*/ /*LATIN SMALL LETTER O WITH MACRON AND ACUTE*/
+  0x1E54, /*U+1E54*/ /**/
+  0x1E54, /*U+1E55*/ /*LATIN SMALL LETTER P WITH ACUTE*/
+  0x1E56, /*U+1E56*/ /**/
+  0x1E56, /*U+1E57*/ /*LATIN SMALL LETTER P WITH DOT ABOVE*/
+  0x1E58, /*U+1E58*/ /**/
+  0x1E58, /*U+1E59*/ /*LATIN SMALL LETTER R WITH DOT ABOVE*/
+  0x1E5A, /*U+1E5A*/ /**/
+  0x1E5A, /*U+1E5B*/ /*LATIN SMALL LETTER R WITH DOT BELOW*/
+  0x1E5C, /*U+1E5C*/ /**/
+  0x1E5C, /*U+1E5D*/ /*LATIN SMALL LETTER R WITH DOT BELOW AND MACRON*/
+  0x1E5E, /*U+1E5E*/ /**/
+  0x1E5E, /*U+1E5F*/ /*LATIN SMALL LETTER R WITH LINE BELOW*/
+  0x1E60, /*U+1E60*/ /**/
+  0x1E60, /*U+1E61*/ /*LATIN SMALL LETTER S WITH DOT ABOVE*/
+  0x1E62, /*U+1E62*/ /**/
+  0x1E62, /*U+1E63*/ /*LATIN SMALL LETTER S WITH DOT BELOW*/
+  0x1E64, /*U+1E64*/ /**/
+  0x1E64, /*U+1E65*/ /*LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE*/
+  0x1E66, /*U+1E66*/ /**/
+  0x1E66, /*U+1E67*/ /*LATIN SMALL LETTER S WITH CARON AND DOT ABOVE*/
+  0x1E68, /*U+1E68*/ /**/
+  0x1E68, /*U+1E69*/ /*LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE*/
+  0x1E6A, /*U+1E6A*/ /**/
+  0x1E6A, /*U+1E6B*/ /*LATIN SMALL LETTER T WITH DOT ABOVE*/
+  0x1E6C, /*U+1E6C*/ /**/
+  0x1E6C, /*U+1E6D*/ /*LATIN SMALL LETTER T WITH DOT BELOW*/
+  0x1E6E, /*U+1E6E*/ /**/
+  0x1E6E, /*U+1E6F*/ /*LATIN SMALL LETTER T WITH LINE BELOW*/
+  0x1E70, /*U+1E70*/ /**/
+  0x1E70, /*U+1E71*/ /*LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW*/
+  0x1E72, /*U+1E72*/ /**/
+  0x1E72, /*U+1E73*/ /*LATIN SMALL LETTER U WITH DIAERESIS BELOW*/
+  0x1E74, /*U+1E74*/ /**/
+  0x1E74, /*U+1E75*/ /*LATIN SMALL LETTER U WITH TILDE BELOW*/
+  0x1E76, /*U+1E76*/ /**/
+  0x1E76, /*U+1E77*/ /*LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW*/
+  0x1E78, /*U+1E78*/ /**/
+  0x1E78, /*U+1E79*/ /*LATIN SMALL LETTER U WITH TILDE AND ACUTE*/
+  0x1E7A, /*U+1E7A*/ /**/
+  0x1E7A, /*U+1E7B*/ /*LATIN SMALL LETTER U WITH MACRON AND DIAERESIS*/
+  0x1E7C, /*U+1E7C*/ /**/
+  0x1E7C, /*U+1E7D*/ /*LATIN SMALL LETTER V WITH TILDE*/
+  0x1E7E, /*U+1E7E*/ /**/
+  0x1E7E, /*U+1E7F*/ /*LATIN SMALL LETTER V WITH DOT BELOW*/
+  0x1E80, /*U+1E80*/ /**/
+  0x1E80, /*U+1E81*/ /*LATIN SMALL LETTER W WITH GRAVE*/
+  0x1E82, /*U+1E82*/ /**/
+  0x1E82, /*U+1E83*/ /*LATIN SMALL LETTER W WITH ACUTE*/
+  0x1E84, /*U+1E84*/ /**/
+  0x1E84, /*U+1E85*/ /*LATIN SMALL LETTER W WITH DIAERESIS*/
+  0x1E86, /*U+1E86*/ /**/
+  0x1E86, /*U+1E87*/ /*LATIN SMALL LETTER W WITH DOT ABOVE*/
+  0x1E88, /*U+1E88*/ /**/
+  0x1E88, /*U+1E89*/ /*LATIN SMALL LETTER W WITH DOT BELOW*/
+  0x1E8A, /*U+1E8A*/ /**/
+  0x1E8A, /*U+1E8B*/ /*LATIN SMALL LETTER X WITH DOT ABOVE*/
+  0x1E8C, /*U+1E8C*/ /**/
+  0x1E8C, /*U+1E8D*/ /*LATIN SMALL LETTER X WITH DIAERESIS*/
+  0x1E8E, /*U+1E8E*/ /**/
+  0x1E8E, /*U+1E8F*/ /*LATIN SMALL LETTER Y WITH DOT ABOVE*/
+  0x1E90, /*U+1E90*/ /**/
+  0x1E90, /*U+1E91*/ /*LATIN SMALL LETTER Z WITH CIRCUMFLEX*/
+  0x1E92, /*U+1E92*/ /**/
+  0x1E92, /*U+1E93*/ /*LATIN SMALL LETTER Z WITH DOT BELOW*/
+  0x1E94, /*U+1E94*/ /**/
+  0x1E94, /*U+1E95*/ /*LATIN SMALL LETTER Z WITH LINE BELOW*/
+  0x1E96, /*U+1E96*/ /**/
+  0x1E97, /*U+1E97*/ /**/
+  0x1E98, /*U+1E98*/ /**/
+  0x1E99, /*U+1E99*/ /**/
+  0x1E9A, /*U+1E9A*/ /**/
+  0x1E60, /*U+1E9B*/ /*LATIN SMALL LETTER LONG S WITH DOT ABOVE*/
+  0x1E9C, /*U+1E9C*/ /**/
+  0x1E9D, /*U+1E9D*/ /**/
+  0x1E9E, /*U+1E9E*/ /**/
+  0x1E9F, /*U+1E9F*/ /**/
+  0x1EA0, /*U+1EA0*/ /**/
+  0x1EA0, /*U+1EA1*/ /*LATIN SMALL LETTER A WITH DOT BELOW*/
+  0x1EA2, /*U+1EA2*/ /**/
+  0x1EA2, /*U+1EA3*/ /*LATIN SMALL LETTER A WITH HOOK ABOVE*/
+  0x1EA4, /*U+1EA4*/ /**/
+  0x1EA4, /*U+1EA5*/ /*LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE*/
+  0x1EA6, /*U+1EA6*/ /**/
+  0x1EA6, /*U+1EA7*/ /*LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE*/
+  0x1EA8, /*U+1EA8*/ /**/
+  0x1EA8, /*U+1EA9*/ /*LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE*/
+  0x1EAA, /*U+1EAA*/ /**/
+  0x1EAA, /*U+1EAB*/ /*LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE*/
+  0x1EAC, /*U+1EAC*/ /**/
+  0x1EAC, /*U+1EAD*/ /*LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW*/
+  0x1EAE, /*U+1EAE*/ /**/
+  0x1EAE, /*U+1EAF*/ /*LATIN SMALL LETTER A WITH BREVE AND ACUTE*/
+  0x1EB0, /*U+1EB0*/ /**/
+  0x1EB0, /*U+1EB1*/ /*LATIN SMALL LETTER A WITH BREVE AND GRAVE*/
+  0x1EB2, /*U+1EB2*/ /**/
+  0x1EB2, /*U+1EB3*/ /*LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE*/
+  0x1EB4, /*U+1EB4*/ /**/
+  0x1EB4, /*U+1EB5*/ /*LATIN SMALL LETTER A WITH BREVE AND TILDE*/
+  0x1EB6, /*U+1EB6*/ /**/
+  0x1EB6, /*U+1EB7*/ /*LATIN SMALL LETTER A WITH BREVE AND DOT BELOW*/
+  0x1EB8, /*U+1EB8*/ /**/
+  0x1EB8, /*U+1EB9*/ /*LATIN SMALL LETTER E WITH DOT BELOW*/
+  0x1EBA, /*U+1EBA*/ /**/
+  0x1EBA, /*U+1EBB*/ /*LATIN SMALL LETTER E WITH HOOK ABOVE*/
+  0x1EBC, /*U+1EBC*/ /**/
+  0x1EBC, /*U+1EBD*/ /*LATIN SMALL LETTER E WITH TILDE*/
+  0x1EBE, /*U+1EBE*/ /**/
+  0x1EBE, /*U+1EBF*/ /*LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE*/
+  0x1EC0, /*U+1EC0*/ /**/
+  0x1EC0, /*U+1EC1*/ /*LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE*/
+  0x1EC2, /*U+1EC2*/ /**/
+  0x1EC2, /*U+1EC3*/ /*LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE*/
+  0x1EC4, /*U+1EC4*/ /**/
+  0x1EC4, /*U+1EC5*/ /*LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE*/
+  0x1EC6, /*U+1EC6*/ /**/
+  0x1EC6, /*U+1EC7*/ /*LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW*/
+  0x1EC8, /*U+1EC8*/ /**/
+  0x1EC8, /*U+1EC9*/ /*LATIN SMALL LETTER I WITH HOOK ABOVE*/
+  0x1ECA, /*U+1ECA*/ /**/
+  0x1ECA, /*U+1ECB*/ /*LATIN SMALL LETTER I WITH DOT BELOW*/
+  0x1ECC, /*U+1ECC*/ /**/
+  0x1ECC, /*U+1ECD*/ /*LATIN SMALL LETTER O WITH DOT BELOW*/
+  0x1ECE, /*U+1ECE*/ /**/
+  0x1ECE, /*U+1ECF*/ /*LATIN SMALL LETTER O WITH HOOK ABOVE*/
+  0x1ED0, /*U+1ED0*/ /**/
+  0x1ED0, /*U+1ED1*/ /*LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE*/
+  0x1ED2, /*U+1ED2*/ /**/
+  0x1ED2, /*U+1ED3*/ /*LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE*/
+  0x1ED4, /*U+1ED4*/ /**/
+  0x1ED4, /*U+1ED5*/ /*LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE*/
+  0x1ED6, /*U+1ED6*/ /**/
+  0x1ED6, /*U+1ED7*/ /*LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE*/
+  0x1ED8, /*U+1ED8*/ /**/
+  0x1ED8, /*U+1ED9*/ /*LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW*/
+  0x1EDA, /*U+1EDA*/ /**/
+  0x1EDA, /*U+1EDB*/ /*LATIN SMALL LETTER O WITH HORN AND ACUTE*/
+  0x1EDC, /*U+1EDC*/ /**/
+  0x1EDC, /*U+1EDD*/ /*LATIN SMALL LETTER O WITH HORN AND GRAVE*/
+  0x1EDE, /*U+1EDE*/ /**/
+  0x1EDE, /*U+1EDF*/ /*LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE*/
+  0x1EE0, /*U+1EE0*/ /**/
+  0x1EE0, /*U+1EE1*/ /*LATIN SMALL LETTER O WITH HORN AND TILDE*/
+  0x1EE2, /*U+1EE2*/ /**/
+  0x1EE2, /*U+1EE3*/ /*LATIN SMALL LETTER O WITH HORN AND DOT BELOW*/
+  0x1EE4, /*U+1EE4*/ /**/
+  0x1EE4, /*U+1EE5*/ /*LATIN SMALL LETTER U WITH DOT BELOW*/
+  0x1EE6, /*U+1EE6*/ /**/
+  0x1EE6, /*U+1EE7*/ /*LATIN SMALL LETTER U WITH HOOK ABOVE*/
+  0x1EE8, /*U+1EE8*/ /**/
+  0x1EE8, /*U+1EE9*/ /*LATIN SMALL LETTER U WITH HORN AND ACUTE*/
+  0x1EEA, /*U+1EEA*/ /**/
+  0x1EEA, /*U+1EEB*/ /*LATIN SMALL LETTER U WITH HORN AND GRAVE*/
+  0x1EEC, /*U+1EEC*/ /**/
+  0x1EEC, /*U+1EED*/ /*LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE*/
+  0x1EEE, /*U+1EEE*/ /**/
+  0x1EEE, /*U+1EEF*/ /*LATIN SMALL LETTER U WITH HORN AND TILDE*/
+  0x1EF0, /*U+1EF0*/ /**/
+  0x1EF0, /*U+1EF1*/ /*LATIN SMALL LETTER U WITH HORN AND DOT BELOW*/
+  0x1EF2, /*U+1EF2*/ /**/
+  0x1EF2, /*U+1EF3*/ /*LATIN SMALL LETTER Y WITH GRAVE*/
+  0x1EF4, /*U+1EF4*/ /**/
+  0x1EF4, /*U+1EF5*/ /*LATIN SMALL LETTER Y WITH DOT BELOW*/
+  0x1EF6, /*U+1EF6*/ /**/
+  0x1EF6, /*U+1EF7*/ /*LATIN SMALL LETTER Y WITH HOOK ABOVE*/
+  0x1EF8, /*U+1EF8*/ /**/
+  0x1EF8, /*U+1EF9*/ /*LATIN SMALL LETTER Y WITH TILDE*/
+  0x1EFA, /*U+1EFA*/ /**/
+  0x1EFA, /*U+1EFB*/ /*LATIN SMALL LETTER MIDDLE-WELSH LL*/
+  0x1EFC, /*U+1EFC*/ /**/
+  0x1EFC, /*U+1EFD*/ /*LATIN SMALL LETTER MIDDLE-WELSH V*/
+  0x1EFE, /*U+1EFE*/ /**/
+  0x1EFE, /*U+1EFF*/ /*LATIN SMALL LETTER Y WITH LOOP*/
+  0x1F08, /*U+1F00*/ /*GREEK SMALL LETTER ALPHA WITH PSILI*/
+  0x1F09, /*U+1F01*/ /*GREEK SMALL LETTER ALPHA WITH DASIA*/
+  0x1F0A, /*U+1F02*/ /*GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA*/
+  0x1F0B, /*U+1F03*/ /*GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA*/
+  0x1F0C, /*U+1F04*/ /*GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA*/
+  0x1F0D, /*U+1F05*/ /*GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA*/
+  0x1F0E, /*U+1F06*/ /*GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI*/
+  0x1F0F, /*U+1F07*/ /*GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI*/
+  0x1F08, /*U+1F08*/ /**/
+  0x1F09, /*U+1F09*/ /**/
+  0x1F0A, /*U+1F0A*/ /**/
+  0x1F0B, /*U+1F0B*/ /**/
+  0x1F0C, /*U+1F0C*/ /**/
+  0x1F0D, /*U+1F0D*/ /**/
+  0x1F0E, /*U+1F0E*/ /**/
+  0x1F0F, /*U+1F0F*/ /**/
+  0x1F18, /*U+1F10*/ /*GREEK SMALL LETTER EPSILON WITH PSILI*/
+  0x1F19, /*U+1F11*/ /*GREEK SMALL LETTER EPSILON WITH DASIA*/
+  0x1F1A, /*U+1F12*/ /*GREEK SMALL LETTER EPSILON WITH PSILI AND VARIA*/
+  0x1F1B, /*U+1F13*/ /*GREEK SMALL LETTER EPSILON WITH DASIA AND VARIA*/
+  0x1F1C, /*U+1F14*/ /*GREEK SMALL LETTER EPSILON WITH PSILI AND OXIA*/
+  0x1F1D, /*U+1F15*/ /*GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA*/
+  0x1F16, /*U+1F16*/ /**/
+  0x1F17, /*U+1F17*/ /**/
+  0x1F18, /*U+1F18*/ /**/
+  0x1F19, /*U+1F19*/ /**/
+  0x1F1A, /*U+1F1A*/ /**/
+  0x1F1B, /*U+1F1B*/ /**/
+  0x1F1C, /*U+1F1C*/ /**/
+  0x1F1D, /*U+1F1D*/ /**/
+  0x1F1E, /*U+1F1E*/ /**/
+  0x1F1F, /*U+1F1F*/ /**/
+  0x1F28, /*U+1F20*/ /*GREEK SMALL LETTER ETA WITH PSILI*/
+  0x1F29, /*U+1F21*/ /*GREEK SMALL LETTER ETA WITH DASIA*/
+  0x1F2A, /*U+1F22*/ /*GREEK SMALL LETTER ETA WITH PSILI AND VARIA*/
+  0x1F2B, /*U+1F23*/ /*GREEK SMALL LETTER ETA WITH DASIA AND VARIA*/
+  0x1F2C, /*U+1F24*/ /*GREEK SMALL LETTER ETA WITH PSILI AND OXIA*/
+  0x1F2D, /*U+1F25*/ /*GREEK SMALL LETTER ETA WITH DASIA AND OXIA*/
+  0x1F2E, /*U+1F26*/ /*GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI*/
+  0x1F2F, /*U+1F27*/ /*GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI*/
+  0x1F28, /*U+1F28*/ /**/
+  0x1F29, /*U+1F29*/ /**/
+  0x1F2A, /*U+1F2A*/ /**/
+  0x1F2B, /*U+1F2B*/ /**/
+  0x1F2C, /*U+1F2C*/ /**/
+  0x1F2D, /*U+1F2D*/ /**/
+  0x1F2E, /*U+1F2E*/ /**/
+  0x1F2F, /*U+1F2F*/ /**/
+  0x1F38, /*U+1F30*/ /*GREEK SMALL LETTER IOTA WITH PSILI*/
+  0x1F39, /*U+1F31*/ /*GREEK SMALL LETTER IOTA WITH DASIA*/
+  0x1F3A, /*U+1F32*/ /*GREEK SMALL LETTER IOTA WITH PSILI AND VARIA*/
+  0x1F3B, /*U+1F33*/ /*GREEK SMALL LETTER IOTA WITH DASIA AND VARIA*/
+  0x1F3C, /*U+1F34*/ /*GREEK SMALL LETTER IOTA WITH PSILI AND OXIA*/
+  0x1F3D, /*U+1F35*/ /*GREEK SMALL LETTER IOTA WITH DASIA AND OXIA*/
+  0x1F3E, /*U+1F36*/ /*GREEK SMALL LETTER IOTA WITH PSILI AND PERISPOMENI*/
+  0x1F3F, /*U+1F37*/ /*GREEK SMALL LETTER IOTA WITH DASIA AND PERISPOMENI*/
+  0x1F38, /*U+1F38*/ /**/
+  0x1F39, /*U+1F39*/ /**/
+  0x1F3A, /*U+1F3A*/ /**/
+  0x1F3B, /*U+1F3B*/ /**/
+  0x1F3C, /*U+1F3C*/ /**/
+  0x1F3D, /*U+1F3D*/ /**/
+  0x1F3E, /*U+1F3E*/ /**/
+  0x1F3F, /*U+1F3F*/ /**/
+  0x1F48, /*U+1F40*/ /*GREEK SMALL LETTER OMICRON WITH PSILI*/
+  0x1F49, /*U+1F41*/ /*GREEK SMALL LETTER OMICRON WITH DASIA*/
+  0x1F4A, /*U+1F42*/ /*GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA*/
+  0x1F4B, /*U+1F43*/ /*GREEK SMALL LETTER OMICRON WITH DASIA AND VARIA*/
+  0x1F4C, /*U+1F44*/ /*GREEK SMALL LETTER OMICRON WITH PSILI AND OXIA*/
+  0x1F4D, /*U+1F45*/ /*GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA*/
+  0x1F46, /*U+1F46*/ /**/
+  0x1F47, /*U+1F47*/ /**/
+  0x1F48, /*U+1F48*/ /**/
+  0x1F49, /*U+1F49*/ /**/
+  0x1F4A, /*U+1F4A*/ /**/
+  0x1F4B, /*U+1F4B*/ /**/
+  0x1F4C, /*U+1F4C*/ /**/
+  0x1F4D, /*U+1F4D*/ /**/
+  0x1F4E, /*U+1F4E*/ /**/
+  0x1F4F, /*U+1F4F*/ /**/
+  0x1F50, /*U+1F50*/ /**/
+  0x1F59, /*U+1F51*/ /*GREEK SMALL LETTER UPSILON WITH DASIA*/
+  0x1F52, /*U+1F52*/ /**/
+  0x1F5B, /*U+1F53*/ /*GREEK SMALL LETTER UPSILON WITH DASIA AND VARIA*/
+  0x1F54, /*U+1F54*/ /**/
+  0x1F5D, /*U+1F55*/ /*GREEK SMALL LETTER UPSILON WITH DASIA AND OXIA*/
+  0x1F56, /*U+1F56*/ /**/
+  0x1F5F, /*U+1F57*/ /*GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI*/
+  0x1F58, /*U+1F58*/ /**/
+  0x1F59, /*U+1F59*/ /**/
+  0x1F5A, /*U+1F5A*/ /**/
+  0x1F5B, /*U+1F5B*/ /**/
+  0x1F5C, /*U+1F5C*/ /**/
+  0x1F5D, /*U+1F5D*/ /**/
+  0x1F5E, /*U+1F5E*/ /**/
+  0x1F5F, /*U+1F5F*/ /**/
+  0x1F68, /*U+1F60*/ /*GREEK SMALL LETTER OMEGA WITH PSILI*/
+  0x1F69, /*U+1F61*/ /*GREEK SMALL LETTER OMEGA WITH DASIA*/
+  0x1F6A, /*U+1F62*/ /*GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA*/
+  0x1F6B, /*U+1F63*/ /*GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA*/
+  0x1F6C, /*U+1F64*/ /*GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA*/
+  0x1F6D, /*U+1F65*/ /*GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA*/
+  0x1F6E, /*U+1F66*/ /*GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI*/
+  0x1F6F, /*U+1F67*/ /*GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI*/
+  0x1F68, /*U+1F68*/ /**/
+  0x1F69, /*U+1F69*/ /**/
+  0x1F6A, /*U+1F6A*/ /**/
+  0x1F6B, /*U+1F6B*/ /**/
+  0x1F6C, /*U+1F6C*/ /**/
+  0x1F6D, /*U+1F6D*/ /**/
+  0x1F6E, /*U+1F6E*/ /**/
+  0x1F6F, /*U+1F6F*/ /**/
+  0x1FBA, /*U+1F70*/ /*GREEK SMALL LETTER ALPHA WITH VARIA*/
+  0x1FBB, /*U+1F71*/ /*GREEK SMALL LETTER ALPHA WITH OXIA*/
+  0x1FC8, /*U+1F72*/ /*GREEK SMALL LETTER EPSILON WITH VARIA*/
+  0x1FC9, /*U+1F73*/ /*GREEK SMALL LETTER EPSILON WITH OXIA*/
+  0x1FCA, /*U+1F74*/ /*GREEK SMALL LETTER ETA WITH VARIA*/
+  0x1FCB, /*U+1F75*/ /*GREEK SMALL LETTER ETA WITH OXIA*/
+  0x1FDA, /*U+1F76*/ /*GREEK SMALL LETTER IOTA WITH VARIA*/
+  0x1FDB, /*U+1F77*/ /*GREEK SMALL LETTER IOTA WITH OXIA*/
+  0x1FF8, /*U+1F78*/ /*GREEK SMALL LETTER OMICRON WITH VARIA*/
+  0x1FF9, /*U+1F79*/ /*GREEK SMALL LETTER OMICRON WITH OXIA*/
+  0x1FEA, /*U+1F7A*/ /*GREEK SMALL LETTER UPSILON WITH VARIA*/
+  0x1FEB, /*U+1F7B*/ /*GREEK SMALL LETTER UPSILON WITH OXIA*/
+  0x1FFA, /*U+1F7C*/ /*GREEK SMALL LETTER OMEGA WITH VARIA*/
+  0x1FFB, /*U+1F7D*/ /*GREEK SMALL LETTER OMEGA WITH OXIA*/
+  0x1F7E, /*U+1F7E*/ /**/
+  0x1F7F, /*U+1F7F*/ /**/
+  0x1F88, /*U+1F80*/ /*GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI*/
+  0x1F89, /*U+1F81*/ /*GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI*/
+  0x1F8A, /*U+1F82*/ /*GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI*/
+  0x1F8B, /*U+1F83*/ /*GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI*/
+  0x1F8C, /*U+1F84*/ /*GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI*/
+  0x1F8D, /*U+1F85*/ /*GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI*/
+  0x1F8E, /*U+1F86*/ /*GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI*/
+  0x1F8F, /*U+1F87*/ /*GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI*/
+  0x1F88, /*U+1F88*/ /**/
+  0x1F89, /*U+1F89*/ /**/
+  0x1F8A, /*U+1F8A*/ /**/
+  0x1F8B, /*U+1F8B*/ /**/
+  0x1F8C, /*U+1F8C*/ /**/
+  0x1F8D, /*U+1F8D*/ /**/
+  0x1F8E, /*U+1F8E*/ /**/
+  0x1F8F, /*U+1F8F*/ /**/
+  0x1F98, /*U+1F90*/ /*GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI*/
+  0x1F99, /*U+1F91*/ /*GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI*/
+  0x1F9A, /*U+1F92*/ /*GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI*/
+  0x1F9B, /*U+1F93*/ /*GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI*/
+  0x1F9C, /*U+1F94*/ /*GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI*/
+  0x1F9D, /*U+1F95*/ /*GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI*/
+  0x1F9E, /*U+1F96*/ /*GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI*/
+  0x1F9F, /*U+1F97*/ /*GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI*/
+  0x1F98, /*U+1F98*/ /**/
+  0x1F99, /*U+1F99*/ /**/
+  0x1F9A, /*U+1F9A*/ /**/
+  0x1F9B, /*U+1F9B*/ /**/
+  0x1F9C, /*U+1F9C*/ /**/
+  0x1F9D, /*U+1F9D*/ /**/
+  0x1F9E, /*U+1F9E*/ /**/
+  0x1F9F, /*U+1F9F*/ /**/
+  0x1FA8, /*U+1FA0*/ /*GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI*/
+  0x1FA9, /*U+1FA1*/ /*GREEK SMALL LETTER OMEGA WITH DASIA AND YPOGEGRAMMENI*/
+  0x1FAA, /*U+1FA2*/ /*GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA AND YPOGEGRAMMENI*/
+  0x1FAB, /*U+1FA3*/ /*GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA AND YPOGEGRAMMENI*/
+  0x1FAC, /*U+1FA4*/ /*GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA AND YPOGEGRAMMENI*/
+  0x1FAD, /*U+1FA5*/ /*GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA AND YPOGEGRAMMENI*/
+  0x1FAE, /*U+1FA6*/ /*GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI*/
+  0x1FAF, /*U+1FA7*/ /*GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI*/
+  0x1FA8, /*U+1FA8*/ /**/
+  0x1FA9, /*U+1FA9*/ /**/
+  0x1FAA, /*U+1FAA*/ /**/
+  0x1FAB, /*U+1FAB*/ /**/
+  0x1FAC, /*U+1FAC*/ /**/
+  0x1FAD, /*U+1FAD*/ /**/
+  0x1FAE, /*U+1FAE*/ /**/
+  0x1FAF, /*U+1FAF*/ /**/
+  0x1FB8, /*U+1FB0*/ /*GREEK SMALL LETTER ALPHA WITH VRACHY*/
+  0x1FB9, /*U+1FB1*/ /*GREEK SMALL LETTER ALPHA WITH MACRON*/
+  0x1FB2, /*U+1FB2*/ /**/
+  0x1FBC, /*U+1FB3*/ /*GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI*/
+  0x1FB4, /*U+1FB4*/ /**/
+  0x1FB5, /*U+1FB5*/ /**/
+  0x1FB6, /*U+1FB6*/ /**/
+  0x1FB7, /*U+1FB7*/ /**/
+  0x1FB8, /*U+1FB8*/ /**/
+  0x1FB9, /*U+1FB9*/ /**/
+  0x1FBA, /*U+1FBA*/ /**/
+  0x1FBB, /*U+1FBB*/ /**/
+  0x1FBC, /*U+1FBC*/ /**/
+  0x1FBD, /*U+1FBD*/ /**/
+  0x0399, /*U+1FBE*/ /*GREEK PROSGEGRAMMENI*/
+  0x1FBF, /*U+1FBF*/ /**/
+  0x1FC0, /*U+1FC0*/ /**/
+  0x1FC1, /*U+1FC1*/ /**/
+  0x1FC2, /*U+1FC2*/ /**/
+  0x1FCC, /*U+1FC3*/ /*GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI*/
+  0x1FC4, /*U+1FC4*/ /**/
+  0x1FC5, /*U+1FC5*/ /**/
+  0x1FC6, /*U+1FC6*/ /**/
+  0x1FC7, /*U+1FC7*/ /**/
+  0x1FC8, /*U+1FC8*/ /**/
+  0x1FC9, /*U+1FC9*/ /**/
+  0x1FCA, /*U+1FCA*/ /**/
+  0x1FCB, /*U+1FCB*/ /**/
+  0x1FCC, /*U+1FCC*/ /**/
+  0x1FCD, /*U+1FCD*/ /**/
+  0x1FCE, /*U+1FCE*/ /**/
+  0x1FCF, /*U+1FCF*/ /**/
+  0x1FD8, /*U+1FD0*/ /*GREEK SMALL LETTER IOTA WITH VRACHY*/
+  0x1FD9, /*U+1FD1*/ /*GREEK SMALL LETTER IOTA WITH MACRON*/
+  0x1FD2, /*U+1FD2*/ /**/
+  0x1FD3, /*U+1FD3*/ /**/
+  0x1FD4, /*U+1FD4*/ /**/
+  0x1FD5, /*U+1FD5*/ /**/
+  0x1FD6, /*U+1FD6*/ /**/
+  0x1FD7, /*U+1FD7*/ /**/
+  0x1FD8, /*U+1FD8*/ /**/
+  0x1FD9, /*U+1FD9*/ /**/
+  0x1FDA, /*U+1FDA*/ /**/
+  0x1FDB, /*U+1FDB*/ /**/
+  0x1FDC, /*U+1FDC*/ /**/
+  0x1FDD, /*U+1FDD*/ /**/
+  0x1FDE, /*U+1FDE*/ /**/
+  0x1FDF, /*U+1FDF*/ /**/
+  0x1FE8, /*U+1FE0*/ /*GREEK SMALL LETTER UPSILON WITH VRACHY*/
+  0x1FE9, /*U+1FE1*/ /*GREEK SMALL LETTER UPSILON WITH MACRON*/
+  0x1FE2, /*U+1FE2*/ /**/
+  0x1FE3, /*U+1FE3*/ /**/
+  0x1FE4, /*U+1FE4*/ /**/
+  0x1FEC, /*U+1FE5*/ /*GREEK SMALL LETTER RHO WITH DASIA*/
+  0x1FE6, /*U+1FE6*/ /**/
+  0x1FE7, /*U+1FE7*/ /**/
+  0x1FE8, /*U+1FE8*/ /**/
+  0x1FE9, /*U+1FE9*/ /**/
+  0x1FEA, /*U+1FEA*/ /**/
+  0x1FEB, /*U+1FEB*/ /**/
+  0x1FEC, /*U+1FEC*/ /**/
+  0x1FED, /*U+1FED*/ /**/
+  0x1FEE, /*U+1FEE*/ /**/
+  0x1FEF, /*U+1FEF*/ /**/
+  0x1FF0, /*U+1FF0*/ /**/
+  0x1FF1, /*U+1FF1*/ /**/
+  0x1FF2, /*U+1FF2*/ /**/
+  0x1FFC, /*U+1FF3*/ /*GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI*/
+  0x1FF4, /*U+1FF4*/ /**/
+  0x1FF5, /*U+1FF5*/ /**/
+  0x1FF6, /*U+1FF6*/ /**/
+  0x1FF7, /*U+1FF7*/ /**/
+  0x1FF8, /*U+1FF8*/ /**/
+  0x1FF9, /*U+1FF9*/ /**/
+  0x1FFA, /*U+1FFA*/ /**/
+  0x1FFB, /*U+1FFB*/ /**/
+  0x1FFC, /*U+1FFC*/ /**/
+  0x1FFD, /*U+1FFD*/ /**/
+  0x1FFE, /*U+1FFE*/ /**/
+  0x1FFF, /*U+1FFF*/ /**/
+};
+
+static const u_int16_t upper_table_5[128] = {
+  0x2140, /*U+2140*/ /**/
+  0x2141, /*U+2141*/ /**/
+  0x2142, /*U+2142*/ /**/
+  0x2143, /*U+2143*/ /**/
+  0x2144, /*U+2144*/ /**/
+  0x2145, /*U+2145*/ /**/
+  0x2146, /*U+2146*/ /**/
+  0x2147, /*U+2147*/ /**/
+  0x2148, /*U+2148*/ /**/
+  0x2149, /*U+2149*/ /**/
+  0x214A, /*U+214A*/ /**/
+  0x214B, /*U+214B*/ /**/
+  0x214C, /*U+214C*/ /**/
+  0x214D, /*U+214D*/ /**/
+  0x2132, /*U+214E*/ /*TURNED SMALL F*/
+  0x214F, /*U+214F*/ /**/
+  0x2150, /*U+2150*/ /**/
+  0x2151, /*U+2151*/ /**/
+  0x2152, /*U+2152*/ /**/
+  0x2153, /*U+2153*/ /**/
+  0x2154, /*U+2154*/ /**/
+  0x2155, /*U+2155*/ /**/
+  0x2156, /*U+2156*/ /**/
+  0x2157, /*U+2157*/ /**/
+  0x2158, /*U+2158*/ /**/
+  0x2159, /*U+2159*/ /**/
+  0x215A, /*U+215A*/ /**/
+  0x215B, /*U+215B*/ /**/
+  0x215C, /*U+215C*/ /**/
+  0x215D, /*U+215D*/ /**/
+  0x215E, /*U+215E*/ /**/
+  0x215F, /*U+215F*/ /**/
+  0x2160, /*U+2160*/ /**/
+  0x2161, /*U+2161*/ /**/
+  0x2162, /*U+2162*/ /**/
+  0x2163, /*U+2163*/ /**/
+  0x2164, /*U+2164*/ /**/
+  0x2165, /*U+2165*/ /**/
+  0x2166, /*U+2166*/ /**/
+  0x2167, /*U+2167*/ /**/
+  0x2168, /*U+2168*/ /**/
+  0x2169, /*U+2169*/ /**/
+  0x216A, /*U+216A*/ /**/
+  0x216B, /*U+216B*/ /**/
+  0x216C, /*U+216C*/ /**/
+  0x216D, /*U+216D*/ /**/
+  0x216E, /*U+216E*/ /**/
+  0x216F, /*U+216F*/ /**/
+  0x2160, /*U+2170*/ /*SMALL ROMAN NUMERAL ONE*/
+  0x2161, /*U+2171*/ /*SMALL ROMAN NUMERAL TWO*/
+  0x2162, /*U+2172*/ /*SMALL ROMAN NUMERAL THREE*/
+  0x2163, /*U+2173*/ /*SMALL ROMAN NUMERAL FOUR*/
+  0x2164, /*U+2174*/ /*SMALL ROMAN NUMERAL FIVE*/
+  0x2165, /*U+2175*/ /*SMALL ROMAN NUMERAL SIX*/
+  0x2166, /*U+2176*/ /*SMALL ROMAN NUMERAL SEVEN*/
+  0x2167, /*U+2177*/ /*SMALL ROMAN NUMERAL EIGHT*/
+  0x2168, /*U+2178*/ /*SMALL ROMAN NUMERAL NINE*/
+  0x2169, /*U+2179*/ /*SMALL ROMAN NUMERAL TEN*/
+  0x216A, /*U+217A*/ /*SMALL ROMAN NUMERAL ELEVEN*/
+  0x216B, /*U+217B*/ /*SMALL ROMAN NUMERAL TWELVE*/
+  0x216C, /*U+217C*/ /*SMALL ROMAN NUMERAL FIFTY*/
+  0x216D, /*U+217D*/ /*SMALL ROMAN NUMERAL ONE HUNDRED*/
+  0x216E, /*U+217E*/ /*SMALL ROMAN NUMERAL FIVE HUNDRED*/
+  0x216F, /*U+217F*/ /*SMALL ROMAN NUMERAL ONE THOUSAND*/
+  0x2180, /*U+2180*/ /**/
+  0x2181, /*U+2181*/ /**/
+  0x2182, /*U+2182*/ /**/
+  0x2183, /*U+2183*/ /**/
+  0x2183, /*U+2184*/ /*LATIN SMALL LETTER REVERSED C*/
+  0x2185, /*U+2185*/ /**/
+  0x2186, /*U+2186*/ /**/
+  0x2187, /*U+2187*/ /**/
+  0x2188, /*U+2188*/ /**/
+  0x2189, /*U+2189*/ /**/
+  0x218A, /*U+218A*/ /**/
+  0x218B, /*U+218B*/ /**/
+  0x218C, /*U+218C*/ /**/
+  0x218D, /*U+218D*/ /**/
+  0x218E, /*U+218E*/ /**/
+  0x218F, /*U+218F*/ /**/
+  0x2190, /*U+2190*/ /**/
+  0x2191, /*U+2191*/ /**/
+  0x2192, /*U+2192*/ /**/
+  0x2193, /*U+2193*/ /**/
+  0x2194, /*U+2194*/ /**/
+  0x2195, /*U+2195*/ /**/
+  0x2196, /*U+2196*/ /**/
+  0x2197, /*U+2197*/ /**/
+  0x2198, /*U+2198*/ /**/
+  0x2199, /*U+2199*/ /**/
+  0x219A, /*U+219A*/ /**/
+  0x219B, /*U+219B*/ /**/
+  0x219C, /*U+219C*/ /**/
+  0x219D, /*U+219D*/ /**/
+  0x219E, /*U+219E*/ /**/
+  0x219F, /*U+219F*/ /**/
+  0x21A0, /*U+21A0*/ /**/
+  0x21A1, /*U+21A1*/ /**/
+  0x21A2, /*U+21A2*/ /**/
+  0x21A3, /*U+21A3*/ /**/
+  0x21A4, /*U+21A4*/ /**/
+  0x21A5, /*U+21A5*/ /**/
+  0x21A6, /*U+21A6*/ /**/
+  0x21A7, /*U+21A7*/ /**/
+  0x21A8, /*U+21A8*/ /**/
+  0x21A9, /*U+21A9*/ /**/
+  0x21AA, /*U+21AA*/ /**/
+  0x21AB, /*U+21AB*/ /**/
+  0x21AC, /*U+21AC*/ /**/
+  0x21AD, /*U+21AD*/ /**/
+  0x21AE, /*U+21AE*/ /**/
+  0x21AF, /*U+21AF*/ /**/
+  0x21B0, /*U+21B0*/ /**/
+  0x21B1, /*U+21B1*/ /**/
+  0x21B2, /*U+21B2*/ /**/
+  0x21B3, /*U+21B3*/ /**/
+  0x21B4, /*U+21B4*/ /**/
+  0x21B5, /*U+21B5*/ /**/
+  0x21B6, /*U+21B6*/ /**/
+  0x21B7, /*U+21B7*/ /**/
+  0x21B8, /*U+21B8*/ /**/
+  0x21B9, /*U+21B9*/ /**/
+  0x21BA, /*U+21BA*/ /**/
+  0x21BB, /*U+21BB*/ /**/
+  0x21BC, /*U+21BC*/ /**/
+  0x21BD, /*U+21BD*/ /**/
+  0x21BE, /*U+21BE*/ /**/
+  0x21BF, /*U+21BF*/ /**/
+};
+
+static const u_int16_t upper_table_6[64] = {
+  0x24C0, /*U+24C0*/ /**/
+  0x24C1, /*U+24C1*/ /**/
+  0x24C2, /*U+24C2*/ /**/
+  0x24C3, /*U+24C3*/ /**/
+  0x24C4, /*U+24C4*/ /**/
+  0x24C5, /*U+24C5*/ /**/
+  0x24C6, /*U+24C6*/ /**/
+  0x24C7, /*U+24C7*/ /**/
+  0x24C8, /*U+24C8*/ /**/
+  0x24C9, /*U+24C9*/ /**/
+  0x24CA, /*U+24CA*/ /**/
+  0x24CB, /*U+24CB*/ /**/
+  0x24CC, /*U+24CC*/ /**/
+  0x24CD, /*U+24CD*/ /**/
+  0x24CE, /*U+24CE*/ /**/
+  0x24CF, /*U+24CF*/ /**/
+  0x24B6, /*U+24D0*/ /*CIRCLED LATIN SMALL LETTER A*/
+  0x24B7, /*U+24D1*/ /*CIRCLED LATIN SMALL LETTER B*/
+  0x24B8, /*U+24D2*/ /*CIRCLED LATIN SMALL LETTER C*/
+  0x24B9, /*U+24D3*/ /*CIRCLED LATIN SMALL LETTER D*/
+  0x24BA, /*U+24D4*/ /*CIRCLED LATIN SMALL LETTER E*/
+  0x24BB, /*U+24D5*/ /*CIRCLED LATIN SMALL LETTER F*/
+  0x24BC, /*U+24D6*/ /*CIRCLED LATIN SMALL LETTER G*/
+  0x24BD, /*U+24D7*/ /*CIRCLED LATIN SMALL LETTER H*/
+  0x24BE, /*U+24D8*/ /*CIRCLED LATIN SMALL LETTER I*/
+  0x24BF, /*U+24D9*/ /*CIRCLED LATIN SMALL LETTER J*/
+  0x24C0, /*U+24DA*/ /*CIRCLED LATIN SMALL LETTER K*/
+  0x24C1, /*U+24DB*/ /*CIRCLED LATIN SMALL LETTER L*/
+  0x24C2, /*U+24DC*/ /*CIRCLED LATIN SMALL LETTER M*/
+  0x24C3, /*U+24DD*/ /*CIRCLED LATIN SMALL LETTER N*/
+  0x24C4, /*U+24DE*/ /*CIRCLED LATIN SMALL LETTER O*/
+  0x24C5, /*U+24DF*/ /*CIRCLED LATIN SMALL LETTER P*/
+  0x24C6, /*U+24E0*/ /*CIRCLED LATIN SMALL LETTER Q*/
+  0x24C7, /*U+24E1*/ /*CIRCLED LATIN SMALL LETTER R*/
+  0x24C8, /*U+24E2*/ /*CIRCLED LATIN SMALL LETTER S*/
+  0x24C9, /*U+24E3*/ /*CIRCLED LATIN SMALL LETTER T*/
+  0x24CA, /*U+24E4*/ /*CIRCLED LATIN SMALL LETTER U*/
+  0x24CB, /*U+24E5*/ /*CIRCLED LATIN SMALL LETTER V*/
+  0x24CC, /*U+24E6*/ /*CIRCLED LATIN SMALL LETTER W*/
+  0x24CD, /*U+24E7*/ /*CIRCLED LATIN SMALL LETTER X*/
+  0x24CE, /*U+24E8*/ /*CIRCLED LATIN SMALL LETTER Y*/
+  0x24CF, /*U+24E9*/ /*CIRCLED LATIN SMALL LETTER Z*/
+  0x24EA, /*U+24EA*/ /**/
+  0x24EB, /*U+24EB*/ /**/
+  0x24EC, /*U+24EC*/ /**/
+  0x24ED, /*U+24ED*/ /**/
+  0x24EE, /*U+24EE*/ /**/
+  0x24EF, /*U+24EF*/ /**/
+  0x24F0, /*U+24F0*/ /**/
+  0x24F1, /*U+24F1*/ /**/
+  0x24F2, /*U+24F2*/ /**/
+  0x24F3, /*U+24F3*/ /**/
+  0x24F4, /*U+24F4*/ /**/
+  0x24F5, /*U+24F5*/ /**/
+  0x24F6, /*U+24F6*/ /**/
+  0x24F7, /*U+24F7*/ /**/
+  0x24F8, /*U+24F8*/ /**/
+  0x24F9, /*U+24F9*/ /**/
+  0x24FA, /*U+24FA*/ /**/
+  0x24FB, /*U+24FB*/ /**/
+  0x24FC, /*U+24FC*/ /**/
+  0x24FD, /*U+24FD*/ /**/
+  0x24FE, /*U+24FE*/ /**/
+  0x24FF, /*U+24FF*/ /**/
+};
+
+static const u_int16_t upper_table_7[320] = {
+  0x2C00, /*U+2C00*/ /**/
+  0x2C01, /*U+2C01*/ /**/
+  0x2C02, /*U+2C02*/ /**/
+  0x2C03, /*U+2C03*/ /**/
+  0x2C04, /*U+2C04*/ /**/
+  0x2C05, /*U+2C05*/ /**/
+  0x2C06, /*U+2C06*/ /**/
+  0x2C07, /*U+2C07*/ /**/
+  0x2C08, /*U+2C08*/ /**/
+  0x2C09, /*U+2C09*/ /**/
+  0x2C0A, /*U+2C0A*/ /**/
+  0x2C0B, /*U+2C0B*/ /**/
+  0x2C0C, /*U+2C0C*/ /**/
+  0x2C0D, /*U+2C0D*/ /**/
+  0x2C0E, /*U+2C0E*/ /**/
+  0x2C0F, /*U+2C0F*/ /**/
+  0x2C10, /*U+2C10*/ /**/
+  0x2C11, /*U+2C11*/ /**/
+  0x2C12, /*U+2C12*/ /**/
+  0x2C13, /*U+2C13*/ /**/
+  0x2C14, /*U+2C14*/ /**/
+  0x2C15, /*U+2C15*/ /**/
+  0x2C16, /*U+2C16*/ /**/
+  0x2C17, /*U+2C17*/ /**/
+  0x2C18, /*U+2C18*/ /**/
+  0x2C19, /*U+2C19*/ /**/
+  0x2C1A, /*U+2C1A*/ /**/
+  0x2C1B, /*U+2C1B*/ /**/
+  0x2C1C, /*U+2C1C*/ /**/
+  0x2C1D, /*U+2C1D*/ /**/
+  0x2C1E, /*U+2C1E*/ /**/
+  0x2C1F, /*U+2C1F*/ /**/
+  0x2C20, /*U+2C20*/ /**/
+  0x2C21, /*U+2C21*/ /**/
+  0x2C22, /*U+2C22*/ /**/
+  0x2C23, /*U+2C23*/ /**/
+  0x2C24, /*U+2C24*/ /**/
+  0x2C25, /*U+2C25*/ /**/
+  0x2C26, /*U+2C26*/ /**/
+  0x2C27, /*U+2C27*/ /**/
+  0x2C28, /*U+2C28*/ /**/
+  0x2C29, /*U+2C29*/ /**/
+  0x2C2A, /*U+2C2A*/ /**/
+  0x2C2B, /*U+2C2B*/ /**/
+  0x2C2C, /*U+2C2C*/ /**/
+  0x2C2D, /*U+2C2D*/ /**/
+  0x2C2E, /*U+2C2E*/ /**/
+  0x2C2F, /*U+2C2F*/ /**/
+  0x2C00, /*U+2C30*/ /*GLAGOLITIC SMALL LETTER AZU*/
+  0x2C01, /*U+2C31*/ /*GLAGOLITIC SMALL LETTER BUKY*/
+  0x2C02, /*U+2C32*/ /*GLAGOLITIC SMALL LETTER VEDE*/
+  0x2C03, /*U+2C33*/ /*GLAGOLITIC SMALL LETTER GLAGOLI*/
+  0x2C04, /*U+2C34*/ /*GLAGOLITIC SMALL LETTER DOBRO*/
+  0x2C05, /*U+2C35*/ /*GLAGOLITIC SMALL LETTER YESTU*/
+  0x2C06, /*U+2C36*/ /*GLAGOLITIC SMALL LETTER ZHIVETE*/
+  0x2C07, /*U+2C37*/ /*GLAGOLITIC SMALL LETTER DZELO*/
+  0x2C08, /*U+2C38*/ /*GLAGOLITIC SMALL LETTER ZEMLJA*/
+  0x2C09, /*U+2C39*/ /*GLAGOLITIC SMALL LETTER IZHE*/
+  0x2C0A, /*U+2C3A*/ /*GLAGOLITIC SMALL LETTER INITIAL IZHE*/
+  0x2C0B, /*U+2C3B*/ /*GLAGOLITIC SMALL LETTER I*/
+  0x2C0C, /*U+2C3C*/ /*GLAGOLITIC SMALL LETTER DJERVI*/
+  0x2C0D, /*U+2C3D*/ /*GLAGOLITIC SMALL LETTER KAKO*/
+  0x2C0E, /*U+2C3E*/ /*GLAGOLITIC SMALL LETTER LJUDIJE*/
+  0x2C0F, /*U+2C3F*/ /*GLAGOLITIC SMALL LETTER MYSLITE*/
+  0x2C10, /*U+2C40*/ /*GLAGOLITIC SMALL LETTER NASHI*/
+  0x2C11, /*U+2C41*/ /*GLAGOLITIC SMALL LETTER ONU*/
+  0x2C12, /*U+2C42*/ /*GLAGOLITIC SMALL LETTER POKOJI*/
+  0x2C13, /*U+2C43*/ /*GLAGOLITIC SMALL LETTER RITSI*/
+  0x2C14, /*U+2C44*/ /*GLAGOLITIC SMALL LETTER SLOVO*/
+  0x2C15, /*U+2C45*/ /*GLAGOLITIC SMALL LETTER TVRIDO*/
+  0x2C16, /*U+2C46*/ /*GLAGOLITIC SMALL LETTER UKU*/
+  0x2C17, /*U+2C47*/ /*GLAGOLITIC SMALL LETTER FRITU*/
+  0x2C18, /*U+2C48*/ /*GLAGOLITIC SMALL LETTER HERU*/
+  0x2C19, /*U+2C49*/ /*GLAGOLITIC SMALL LETTER OTU*/
+  0x2C1A, /*U+2C4A*/ /*GLAGOLITIC SMALL LETTER PE*/
+  0x2C1B, /*U+2C4B*/ /*GLAGOLITIC SMALL LETTER SHTA*/
+  0x2C1C, /*U+2C4C*/ /*GLAGOLITIC SMALL LETTER TSI*/
+  0x2C1D, /*U+2C4D*/ /*GLAGOLITIC SMALL LETTER CHRIVI*/
+  0x2C1E, /*U+2C4E*/ /*GLAGOLITIC SMALL LETTER SHA*/
+  0x2C1F, /*U+2C4F*/ /*GLAGOLITIC SMALL LETTER YERU*/
+  0x2C20, /*U+2C50*/ /*GLAGOLITIC SMALL LETTER YERI*/
+  0x2C21, /*U+2C51*/ /*GLAGOLITIC SMALL LETTER YATI*/
+  0x2C22, /*U+2C52*/ /*GLAGOLITIC SMALL LETTER SPIDERY HA*/
+  0x2C23, /*U+2C53*/ /*GLAGOLITIC SMALL LETTER YU*/
+  0x2C24, /*U+2C54*/ /*GLAGOLITIC SMALL LETTER SMALL YUS*/
+  0x2C25, /*U+2C55*/ /*GLAGOLITIC SMALL LETTER SMALL YUS WITH TAIL*/
+  0x2C26, /*U+2C56*/ /*GLAGOLITIC SMALL LETTER YO*/
+  0x2C27, /*U+2C57*/ /*GLAGOLITIC SMALL LETTER IOTATED SMALL YUS*/
+  0x2C28, /*U+2C58*/ /*GLAGOLITIC SMALL LETTER BIG YUS*/
+  0x2C29, /*U+2C59*/ /*GLAGOLITIC SMALL LETTER IOTATED BIG YUS*/
+  0x2C2A, /*U+2C5A*/ /*GLAGOLITIC SMALL LETTER FITA*/
+  0x2C2B, /*U+2C5B*/ /*GLAGOLITIC SMALL LETTER IZHITSA*/
+  0x2C2C, /*U+2C5C*/ /*GLAGOLITIC SMALL LETTER SHTAPIC*/
+  0x2C2D, /*U+2C5D*/ /*GLAGOLITIC SMALL LETTER TROKUTASTI A*/
+  0x2C2E, /*U+2C5E*/ /*GLAGOLITIC SMALL LETTER LATINATE MYSLITE*/
+  0x2C5F, /*U+2C5F*/ /**/
+  0x2C60, /*U+2C60*/ /**/
+  0x2C60, /*U+2C61*/ /*LATIN SMALL LETTER L WITH DOUBLE BAR*/
+  0x2C62, /*U+2C62*/ /**/
+  0x2C63, /*U+2C63*/ /**/
+  0x2C64, /*U+2C64*/ /**/
+  0x023A, /*U+2C65*/ /*LATIN SMALL LETTER A WITH STROKE*/
+  0x023E, /*U+2C66*/ /*LATIN SMALL LETTER T WITH DIAGONAL STROKE*/
+  0x2C67, /*U+2C67*/ /**/
+  0x2C67, /*U+2C68*/ /*LATIN SMALL LETTER H WITH DESCENDER*/
+  0x2C69, /*U+2C69*/ /**/
+  0x2C69, /*U+2C6A*/ /*LATIN SMALL LETTER K WITH DESCENDER*/
+  0x2C6B, /*U+2C6B*/ /**/
+  0x2C6B, /*U+2C6C*/ /*LATIN SMALL LETTER Z WITH DESCENDER*/
+  0x2C6D, /*U+2C6D*/ /**/
+  0x2C6E, /*U+2C6E*/ /**/
+  0x2C6F, /*U+2C6F*/ /**/
+  0x2C70, /*U+2C70*/ /**/
+  0x2C71, /*U+2C71*/ /**/
+  0x2C72, /*U+2C72*/ /**/
+  0x2C72, /*U+2C73*/ /*LATIN SMALL LETTER W WITH HOOK*/
+  0x2C74, /*U+2C74*/ /**/
+  0x2C75, /*U+2C75*/ /**/
+  0x2C75, /*U+2C76*/ /*LATIN SMALL LETTER HALF H*/
+  0x2C77, /*U+2C77*/ /**/
+  0x2C78, /*U+2C78*/ /**/
+  0x2C79, /*U+2C79*/ /**/
+  0x2C7A, /*U+2C7A*/ /**/
+  0x2C7B, /*U+2C7B*/ /**/
+  0x2C7C, /*U+2C7C*/ /**/
+  0x2C7D, /*U+2C7D*/ /**/
+  0x2C7E, /*U+2C7E*/ /**/
+  0x2C7F, /*U+2C7F*/ /**/
+  0x2C80, /*U+2C80*/ /**/
+  0x2C80, /*U+2C81*/ /*COPTIC SMALL LETTER ALFA*/
+  0x2C82, /*U+2C82*/ /**/
+  0x2C82, /*U+2C83*/ /*COPTIC SMALL LETTER VIDA*/
+  0x2C84, /*U+2C84*/ /**/
+  0x2C84, /*U+2C85*/ /*COPTIC SMALL LETTER GAMMA*/
+  0x2C86, /*U+2C86*/ /**/
+  0x2C86, /*U+2C87*/ /*COPTIC SMALL LETTER DALDA*/
+  0x2C88, /*U+2C88*/ /**/
+  0x2C88, /*U+2C89*/ /*COPTIC SMALL LETTER EIE*/
+  0x2C8A, /*U+2C8A*/ /**/
+  0x2C8A, /*U+2C8B*/ /*COPTIC SMALL LETTER SOU*/
+  0x2C8C, /*U+2C8C*/ /**/
+  0x2C8C, /*U+2C8D*/ /*COPTIC SMALL LETTER ZATA*/
+  0x2C8E, /*U+2C8E*/ /**/
+  0x2C8E, /*U+2C8F*/ /*COPTIC SMALL LETTER HATE*/
+  0x2C90, /*U+2C90*/ /**/
+  0x2C90, /*U+2C91*/ /*COPTIC SMALL LETTER THETHE*/
+  0x2C92, /*U+2C92*/ /**/
+  0x2C92, /*U+2C93*/ /*COPTIC SMALL LETTER IAUDA*/
+  0x2C94, /*U+2C94*/ /**/
+  0x2C94, /*U+2C95*/ /*COPTIC SMALL LETTER KAPA*/
+  0x2C96, /*U+2C96*/ /**/
+  0x2C96, /*U+2C97*/ /*COPTIC SMALL LETTER LAULA*/
+  0x2C98, /*U+2C98*/ /**/
+  0x2C98, /*U+2C99*/ /*COPTIC SMALL LETTER MI*/
+  0x2C9A, /*U+2C9A*/ /**/
+  0x2C9A, /*U+2C9B*/ /*COPTIC SMALL LETTER NI*/
+  0x2C9C, /*U+2C9C*/ /**/
+  0x2C9C, /*U+2C9D*/ /*COPTIC SMALL LETTER KSI*/
+  0x2C9E, /*U+2C9E*/ /**/
+  0x2C9E, /*U+2C9F*/ /*COPTIC SMALL LETTER O*/
+  0x2CA0, /*U+2CA0*/ /**/
+  0x2CA0, /*U+2CA1*/ /*COPTIC SMALL LETTER PI*/
+  0x2CA2, /*U+2CA2*/ /**/
+  0x2CA2, /*U+2CA3*/ /*COPTIC SMALL LETTER RO*/
+  0x2CA4, /*U+2CA4*/ /**/
+  0x2CA4, /*U+2CA5*/ /*COPTIC SMALL LETTER SIMA*/
+  0x2CA6, /*U+2CA6*/ /**/
+  0x2CA6, /*U+2CA7*/ /*COPTIC SMALL LETTER TAU*/
+  0x2CA8, /*U+2CA8*/ /**/
+  0x2CA8, /*U+2CA9*/ /*COPTIC SMALL LETTER UA*/
+  0x2CAA, /*U+2CAA*/ /**/
+  0x2CAA, /*U+2CAB*/ /*COPTIC SMALL LETTER FI*/
+  0x2CAC, /*U+2CAC*/ /**/
+  0x2CAC, /*U+2CAD*/ /*COPTIC SMALL LETTER KHI*/
+  0x2CAE, /*U+2CAE*/ /**/
+  0x2CAE, /*U+2CAF*/ /*COPTIC SMALL LETTER PSI*/
+  0x2CB0, /*U+2CB0*/ /**/
+  0x2CB0, /*U+2CB1*/ /*COPTIC SMALL LETTER OOU*/
+  0x2CB2, /*U+2CB2*/ /**/
+  0x2CB2, /*U+2CB3*/ /*COPTIC SMALL LETTER DIALECT-P ALEF*/
+  0x2CB4, /*U+2CB4*/ /**/
+  0x2CB4, /*U+2CB5*/ /*COPTIC SMALL LETTER OLD COPTIC AIN*/
+  0x2CB6, /*U+2CB6*/ /**/
+  0x2CB6, /*U+2CB7*/ /*COPTIC SMALL LETTER CRYPTOGRAMMIC EIE*/
+  0x2CB8, /*U+2CB8*/ /**/
+  0x2CB8, /*U+2CB9*/ /*COPTIC SMALL LETTER DIALECT-P KAPA*/
+  0x2CBA, /*U+2CBA*/ /**/
+  0x2CBA, /*U+2CBB*/ /*COPTIC SMALL LETTER DIALECT-P NI*/
+  0x2CBC, /*U+2CBC*/ /**/
+  0x2CBC, /*U+2CBD*/ /*COPTIC SMALL LETTER CRYPTOGRAMMIC NI*/
+  0x2CBE, /*U+2CBE*/ /**/
+  0x2CBE, /*U+2CBF*/ /*COPTIC SMALL LETTER OLD COPTIC OOU*/
+  0x2CC0, /*U+2CC0*/ /**/
+  0x2CC0, /*U+2CC1*/ /*COPTIC SMALL LETTER SAMPI*/
+  0x2CC2, /*U+2CC2*/ /**/
+  0x2CC2, /*U+2CC3*/ /*COPTIC SMALL LETTER CROSSED SHEI*/
+  0x2CC4, /*U+2CC4*/ /**/
+  0x2CC4, /*U+2CC5*/ /*COPTIC SMALL LETTER OLD COPTIC SHEI*/
+  0x2CC6, /*U+2CC6*/ /**/
+  0x2CC6, /*U+2CC7*/ /*COPTIC SMALL LETTER OLD COPTIC ESH*/
+  0x2CC8, /*U+2CC8*/ /**/
+  0x2CC8, /*U+2CC9*/ /*COPTIC SMALL LETTER AKHMIMIC KHEI*/
+  0x2CCA, /*U+2CCA*/ /**/
+  0x2CCA, /*U+2CCB*/ /*COPTIC SMALL LETTER DIALECT-P HORI*/
+  0x2CCC, /*U+2CCC*/ /**/
+  0x2CCC, /*U+2CCD*/ /*COPTIC SMALL LETTER OLD COPTIC HORI*/
+  0x2CCE, /*U+2CCE*/ /**/
+  0x2CCE, /*U+2CCF*/ /*COPTIC SMALL LETTER OLD COPTIC HA*/
+  0x2CD0, /*U+2CD0*/ /**/
+  0x2CD0, /*U+2CD1*/ /*COPTIC SMALL LETTER L-SHAPED HA*/
+  0x2CD2, /*U+2CD2*/ /**/
+  0x2CD2, /*U+2CD3*/ /*COPTIC SMALL LETTER OLD COPTIC HEI*/
+  0x2CD4, /*U+2CD4*/ /**/
+  0x2CD4, /*U+2CD5*/ /*COPTIC SMALL LETTER OLD COPTIC HAT*/
+  0x2CD6, /*U+2CD6*/ /**/
+  0x2CD6, /*U+2CD7*/ /*COPTIC SMALL LETTER OLD COPTIC GANGIA*/
+  0x2CD8, /*U+2CD8*/ /**/
+  0x2CD8, /*U+2CD9*/ /*COPTIC SMALL LETTER OLD COPTIC DJA*/
+  0x2CDA, /*U+2CDA*/ /**/
+  0x2CDA, /*U+2CDB*/ /*COPTIC SMALL LETTER OLD COPTIC SHIMA*/
+  0x2CDC, /*U+2CDC*/ /**/
+  0x2CDC, /*U+2CDD*/ /*COPTIC SMALL LETTER OLD NUBIAN SHIMA*/
+  0x2CDE, /*U+2CDE*/ /**/
+  0x2CDE, /*U+2CDF*/ /*COPTIC SMALL LETTER OLD NUBIAN NGI*/
+  0x2CE0, /*U+2CE0*/ /**/
+  0x2CE0, /*U+2CE1*/ /*COPTIC SMALL LETTER OLD NUBIAN NYI*/
+  0x2CE2, /*U+2CE2*/ /**/
+  0x2CE2, /*U+2CE3*/ /*COPTIC SMALL LETTER OLD NUBIAN WAU*/
+  0x2CE4, /*U+2CE4*/ /**/
+  0x2CE5, /*U+2CE5*/ /**/
+  0x2CE6, /*U+2CE6*/ /**/
+  0x2CE7, /*U+2CE7*/ /**/
+  0x2CE8, /*U+2CE8*/ /**/
+  0x2CE9, /*U+2CE9*/ /**/
+  0x2CEA, /*U+2CEA*/ /**/
+  0x2CEB, /*U+2CEB*/ /**/
+  0x2CEB, /*U+2CEC*/ /*COPTIC SMALL LETTER CRYPTOGRAMMIC SHEI*/
+  0x2CED, /*U+2CED*/ /**/
+  0x2CED, /*U+2CEE*/ /*COPTIC SMALL LETTER CRYPTOGRAMMIC GANGIA*/
+  0x2CEF, /*U+2CEF*/ /**/
+  0x2CF0, /*U+2CF0*/ /**/
+  0x2CF1, /*U+2CF1*/ /**/
+  0x2CF2, /*U+2CF2*/ /**/
+  0x2CF3, /*U+2CF3*/ /**/
+  0x2CF4, /*U+2CF4*/ /**/
+  0x2CF5, /*U+2CF5*/ /**/
+  0x2CF6, /*U+2CF6*/ /**/
+  0x2CF7, /*U+2CF7*/ /**/
+  0x2CF8, /*U+2CF8*/ /**/
+  0x2CF9, /*U+2CF9*/ /**/
+  0x2CFA, /*U+2CFA*/ /**/
+  0x2CFB, /*U+2CFB*/ /**/
+  0x2CFC, /*U+2CFC*/ /**/
+  0x2CFD, /*U+2CFD*/ /**/
+  0x2CFE, /*U+2CFE*/ /**/
+  0x2CFF, /*U+2CFF*/ /**/
+  0x10A0, /*U+2D00*/ /*GEORGIAN SMALL LETTER AN*/
+  0x10A1, /*U+2D01*/ /*GEORGIAN SMALL LETTER BAN*/
+  0x10A2, /*U+2D02*/ /*GEORGIAN SMALL LETTER GAN*/
+  0x10A3, /*U+2D03*/ /*GEORGIAN SMALL LETTER DON*/
+  0x10A4, /*U+2D04*/ /*GEORGIAN SMALL LETTER EN*/
+  0x10A5, /*U+2D05*/ /*GEORGIAN SMALL LETTER VIN*/
+  0x10A6, /*U+2D06*/ /*GEORGIAN SMALL LETTER ZEN*/
+  0x10A7, /*U+2D07*/ /*GEORGIAN SMALL LETTER TAN*/
+  0x10A8, /*U+2D08*/ /*GEORGIAN SMALL LETTER IN*/
+  0x10A9, /*U+2D09*/ /*GEORGIAN SMALL LETTER KAN*/
+  0x10AA, /*U+2D0A*/ /*GEORGIAN SMALL LETTER LAS*/
+  0x10AB, /*U+2D0B*/ /*GEORGIAN SMALL LETTER MAN*/
+  0x10AC, /*U+2D0C*/ /*GEORGIAN SMALL LETTER NAR*/
+  0x10AD, /*U+2D0D*/ /*GEORGIAN SMALL LETTER ON*/
+  0x10AE, /*U+2D0E*/ /*GEORGIAN SMALL LETTER PAR*/
+  0x10AF, /*U+2D0F*/ /*GEORGIAN SMALL LETTER ZHAR*/
+  0x10B0, /*U+2D10*/ /*GEORGIAN SMALL LETTER RAE*/
+  0x10B1, /*U+2D11*/ /*GEORGIAN SMALL LETTER SAN*/
+  0x10B2, /*U+2D12*/ /*GEORGIAN SMALL LETTER TAR*/
+  0x10B3, /*U+2D13*/ /*GEORGIAN SMALL LETTER UN*/
+  0x10B4, /*U+2D14*/ /*GEORGIAN SMALL LETTER PHAR*/
+  0x10B5, /*U+2D15*/ /*GEORGIAN SMALL LETTER KHAR*/
+  0x10B6, /*U+2D16*/ /*GEORGIAN SMALL LETTER GHAN*/
+  0x10B7, /*U+2D17*/ /*GEORGIAN SMALL LETTER QAR*/
+  0x10B8, /*U+2D18*/ /*GEORGIAN SMALL LETTER SHIN*/
+  0x10B9, /*U+2D19*/ /*GEORGIAN SMALL LETTER CHIN*/
+  0x10BA, /*U+2D1A*/ /*GEORGIAN SMALL LETTER CAN*/
+  0x10BB, /*U+2D1B*/ /*GEORGIAN SMALL LETTER JIL*/
+  0x10BC, /*U+2D1C*/ /*GEORGIAN SMALL LETTER CIL*/
+  0x10BD, /*U+2D1D*/ /*GEORGIAN SMALL LETTER CHAR*/
+  0x10BE, /*U+2D1E*/ /*GEORGIAN SMALL LETTER XAN*/
+  0x10BF, /*U+2D1F*/ /*GEORGIAN SMALL LETTER JHAN*/
+  0x10C0, /*U+2D20*/ /*GEORGIAN SMALL LETTER HAE*/
+  0x10C1, /*U+2D21*/ /*GEORGIAN SMALL LETTER HE*/
+  0x10C2, /*U+2D22*/ /*GEORGIAN SMALL LETTER HIE*/
+  0x10C3, /*U+2D23*/ /*GEORGIAN SMALL LETTER WE*/
+  0x10C4, /*U+2D24*/ /*GEORGIAN SMALL LETTER HAR*/
+  0x10C5, /*U+2D25*/ /*GEORGIAN SMALL LETTER HOE*/
+  0x2D26, /*U+2D26*/ /**/
+  0x2D27, /*U+2D27*/ /**/
+  0x2D28, /*U+2D28*/ /**/
+  0x2D29, /*U+2D29*/ /**/
+  0x2D2A, /*U+2D2A*/ /**/
+  0x2D2B, /*U+2D2B*/ /**/
+  0x2D2C, /*U+2D2C*/ /**/
+  0x2D2D, /*U+2D2D*/ /**/
+  0x2D2E, /*U+2D2E*/ /**/
+  0x2D2F, /*U+2D2F*/ /**/
+  0x2D30, /*U+2D30*/ /**/
+  0x2D31, /*U+2D31*/ /**/
+  0x2D32, /*U+2D32*/ /**/
+  0x2D33, /*U+2D33*/ /**/
+  0x2D34, /*U+2D34*/ /**/
+  0x2D35, /*U+2D35*/ /**/
+  0x2D36, /*U+2D36*/ /**/
+  0x2D37, /*U+2D37*/ /**/
+  0x2D38, /*U+2D38*/ /**/
+  0x2D39, /*U+2D39*/ /**/
+  0x2D3A, /*U+2D3A*/ /**/
+  0x2D3B, /*U+2D3B*/ /**/
+  0x2D3C, /*U+2D3C*/ /**/
+  0x2D3D, /*U+2D3D*/ /**/
+  0x2D3E, /*U+2D3E*/ /**/
+  0x2D3F, /*U+2D3F*/ /**/
+};
+
+static const u_int16_t upper_table_8[128] = {
+  0xA640, /*U+A640*/ /**/
+  0xA640, /*U+A641*/ /*CYRILLIC SMALL LETTER ZEMLYA*/
+  0xA642, /*U+A642*/ /**/
+  0xA642, /*U+A643*/ /*CYRILLIC SMALL LETTER DZELO*/
+  0xA644, /*U+A644*/ /**/
+  0xA644, /*U+A645*/ /*CYRILLIC SMALL LETTER REVERSED DZE*/
+  0xA646, /*U+A646*/ /**/
+  0xA646, /*U+A647*/ /*CYRILLIC SMALL LETTER IOTA*/
+  0xA648, /*U+A648*/ /**/
+  0xA648, /*U+A649*/ /*CYRILLIC SMALL LETTER DJERV*/
+  0xA64A, /*U+A64A*/ /**/
+  0xA64A, /*U+A64B*/ /*CYRILLIC SMALL LETTER MONOGRAPH UK*/
+  0xA64C, /*U+A64C*/ /**/
+  0xA64C, /*U+A64D*/ /*CYRILLIC SMALL LETTER BROAD OMEGA*/
+  0xA64E, /*U+A64E*/ /**/
+  0xA64E, /*U+A64F*/ /*CYRILLIC SMALL LETTER NEUTRAL YER*/
+  0xA650, /*U+A650*/ /**/
+  0xA650, /*U+A651*/ /*CYRILLIC SMALL LETTER YERU WITH BACK YER*/
+  0xA652, /*U+A652*/ /**/
+  0xA652, /*U+A653*/ /*CYRILLIC SMALL LETTER IOTIFIED YAT*/
+  0xA654, /*U+A654*/ /**/
+  0xA654, /*U+A655*/ /*CYRILLIC SMALL LETTER REVERSED YU*/
+  0xA656, /*U+A656*/ /**/
+  0xA656, /*U+A657*/ /*CYRILLIC SMALL LETTER IOTIFIED A*/
+  0xA658, /*U+A658*/ /**/
+  0xA658, /*U+A659*/ /*CYRILLIC SMALL LETTER CLOSED LITTLE YUS*/
+  0xA65A, /*U+A65A*/ /**/
+  0xA65A, /*U+A65B*/ /*CYRILLIC SMALL LETTER BLENDED YUS*/
+  0xA65C, /*U+A65C*/ /**/
+  0xA65C, /*U+A65D*/ /*CYRILLIC SMALL LETTER IOTIFIED CLOSED LITTLE YUS*/
+  0xA65E, /*U+A65E*/ /**/
+  0xA65E, /*U+A65F*/ /*CYRILLIC SMALL LETTER YN*/
+  0xA660, /*U+A660*/ /**/
+  0xA660, /*U+A661*/ /*CYRILLIC SMALL LETTER REVERSED TSE*/
+  0xA662, /*U+A662*/ /**/
+  0xA662, /*U+A663*/ /*CYRILLIC SMALL LETTER SOFT DE*/
+  0xA664, /*U+A664*/ /**/
+  0xA664, /*U+A665*/ /*CYRILLIC SMALL LETTER SOFT EL*/
+  0xA666, /*U+A666*/ /**/
+  0xA666, /*U+A667*/ /*CYRILLIC SMALL LETTER SOFT EM*/
+  0xA668, /*U+A668*/ /**/
+  0xA668, /*U+A669*/ /*CYRILLIC SMALL LETTER MONOCULAR O*/
+  0xA66A, /*U+A66A*/ /**/
+  0xA66A, /*U+A66B*/ /*CYRILLIC SMALL LETTER BINOCULAR O*/
+  0xA66C, /*U+A66C*/ /**/
+  0xA66C, /*U+A66D*/ /*CYRILLIC SMALL LETTER DOUBLE MONOCULAR O*/
+  0xA66E, /*U+A66E*/ /**/
+  0xA66F, /*U+A66F*/ /**/
+  0xA670, /*U+A670*/ /**/
+  0xA671, /*U+A671*/ /**/
+  0xA672, /*U+A672*/ /**/
+  0xA673, /*U+A673*/ /**/
+  0xA674, /*U+A674*/ /**/
+  0xA675, /*U+A675*/ /**/
+  0xA676, /*U+A676*/ /**/
+  0xA677, /*U+A677*/ /**/
+  0xA678, /*U+A678*/ /**/
+  0xA679, /*U+A679*/ /**/
+  0xA67A, /*U+A67A*/ /**/
+  0xA67B, /*U+A67B*/ /**/
+  0xA67C, /*U+A67C*/ /**/
+  0xA67D, /*U+A67D*/ /**/
+  0xA67E, /*U+A67E*/ /**/
+  0xA67F, /*U+A67F*/ /**/
+  0xA680, /*U+A680*/ /**/
+  0xA680, /*U+A681*/ /*CYRILLIC SMALL LETTER DWE*/
+  0xA682, /*U+A682*/ /**/
+  0xA682, /*U+A683*/ /*CYRILLIC SMALL LETTER DZWE*/
+  0xA684, /*U+A684*/ /**/
+  0xA684, /*U+A685*/ /*CYRILLIC SMALL LETTER ZHWE*/
+  0xA686, /*U+A686*/ /**/
+  0xA686, /*U+A687*/ /*CYRILLIC SMALL LETTER CCHE*/
+  0xA688, /*U+A688*/ /**/
+  0xA688, /*U+A689*/ /*CYRILLIC SMALL LETTER DZZE*/
+  0xA68A, /*U+A68A*/ /**/
+  0xA68A, /*U+A68B*/ /*CYRILLIC SMALL LETTER TE WITH MIDDLE HOOK*/
+  0xA68C, /*U+A68C*/ /**/
+  0xA68C, /*U+A68D*/ /*CYRILLIC SMALL LETTER TWE*/
+  0xA68E, /*U+A68E*/ /**/
+  0xA68E, /*U+A68F*/ /*CYRILLIC SMALL LETTER TSWE*/
+  0xA690, /*U+A690*/ /**/
+  0xA690, /*U+A691*/ /*CYRILLIC SMALL LETTER TSSE*/
+  0xA692, /*U+A692*/ /**/
+  0xA692, /*U+A693*/ /*CYRILLIC SMALL LETTER TCHE*/
+  0xA694, /*U+A694*/ /**/
+  0xA694, /*U+A695*/ /*CYRILLIC SMALL LETTER HWE*/
+  0xA696, /*U+A696*/ /**/
+  0xA696, /*U+A697*/ /*CYRILLIC SMALL LETTER SHWE*/
+  0xA698, /*U+A698*/ /**/
+  0xA699, /*U+A699*/ /**/
+  0xA69A, /*U+A69A*/ /**/
+  0xA69B, /*U+A69B*/ /**/
+  0xA69C, /*U+A69C*/ /**/
+  0xA69D, /*U+A69D*/ /**/
+  0xA69E, /*U+A69E*/ /**/
+  0xA69F, /*U+A69F*/ /**/
+  0xA6A0, /*U+A6A0*/ /**/
+  0xA6A1, /*U+A6A1*/ /**/
+  0xA6A2, /*U+A6A2*/ /**/
+  0xA6A3, /*U+A6A3*/ /**/
+  0xA6A4, /*U+A6A4*/ /**/
+  0xA6A5, /*U+A6A5*/ /**/
+  0xA6A6, /*U+A6A6*/ /**/
+  0xA6A7, /*U+A6A7*/ /**/
+  0xA6A8, /*U+A6A8*/ /**/
+  0xA6A9, /*U+A6A9*/ /**/
+  0xA6AA, /*U+A6AA*/ /**/
+  0xA6AB, /*U+A6AB*/ /**/
+  0xA6AC, /*U+A6AC*/ /**/
+  0xA6AD, /*U+A6AD*/ /**/
+  0xA6AE, /*U+A6AE*/ /**/
+  0xA6AF, /*U+A6AF*/ /**/
+  0xA6B0, /*U+A6B0*/ /**/
+  0xA6B1, /*U+A6B1*/ /**/
+  0xA6B2, /*U+A6B2*/ /**/
+  0xA6B3, /*U+A6B3*/ /**/
+  0xA6B4, /*U+A6B4*/ /**/
+  0xA6B5, /*U+A6B5*/ /**/
+  0xA6B6, /*U+A6B6*/ /**/
+  0xA6B7, /*U+A6B7*/ /**/
+  0xA6B8, /*U+A6B8*/ /**/
+  0xA6B9, /*U+A6B9*/ /**/
+  0xA6BA, /*U+A6BA*/ /**/
+  0xA6BB, /*U+A6BB*/ /**/
+  0xA6BC, /*U+A6BC*/ /**/
+  0xA6BD, /*U+A6BD*/ /**/
+  0xA6BE, /*U+A6BE*/ /**/
+  0xA6BF, /*U+A6BF*/ /**/
+};
+
+static const u_int16_t upper_table_9[192] = {
+  0xA700, /*U+A700*/ /**/
+  0xA701, /*U+A701*/ /**/
+  0xA702, /*U+A702*/ /**/
+  0xA703, /*U+A703*/ /**/
+  0xA704, /*U+A704*/ /**/
+  0xA705, /*U+A705*/ /**/
+  0xA706, /*U+A706*/ /**/
+  0xA707, /*U+A707*/ /**/
+  0xA708, /*U+A708*/ /**/
+  0xA709, /*U+A709*/ /**/
+  0xA70A, /*U+A70A*/ /**/
+  0xA70B, /*U+A70B*/ /**/
+  0xA70C, /*U+A70C*/ /**/
+  0xA70D, /*U+A70D*/ /**/
+  0xA70E, /*U+A70E*/ /**/
+  0xA70F, /*U+A70F*/ /**/
+  0xA710, /*U+A710*/ /**/
+  0xA711, /*U+A711*/ /**/
+  0xA712, /*U+A712*/ /**/
+  0xA713, /*U+A713*/ /**/
+  0xA714, /*U+A714*/ /**/
+  0xA715, /*U+A715*/ /**/
+  0xA716, /*U+A716*/ /**/
+  0xA717, /*U+A717*/ /**/
+  0xA718, /*U+A718*/ /**/
+  0xA719, /*U+A719*/ /**/
+  0xA71A, /*U+A71A*/ /**/
+  0xA71B, /*U+A71B*/ /**/
+  0xA71C, /*U+A71C*/ /**/
+  0xA71D, /*U+A71D*/ /**/
+  0xA71E, /*U+A71E*/ /**/
+  0xA71F, /*U+A71F*/ /**/
+  0xA720, /*U+A720*/ /**/
+  0xA721, /*U+A721*/ /**/
+  0xA722, /*U+A722*/ /**/
+  0xA722, /*U+A723*/ /*LATIN SMALL LETTER EGYPTOLOGICAL ALEF*/
+  0xA724, /*U+A724*/ /**/
+  0xA724, /*U+A725*/ /*LATIN SMALL LETTER EGYPTOLOGICAL AIN*/
+  0xA726, /*U+A726*/ /**/
+  0xA726, /*U+A727*/ /*LATIN SMALL LETTER HENG*/
+  0xA728, /*U+A728*/ /**/
+  0xA728, /*U+A729*/ /*LATIN SMALL LETTER TZ*/
+  0xA72A, /*U+A72A*/ /**/
+  0xA72A, /*U+A72B*/ /*LATIN SMALL LETTER TRESILLO*/
+  0xA72C, /*U+A72C*/ /**/
+  0xA72C, /*U+A72D*/ /*LATIN SMALL LETTER CUATRILLO*/
+  0xA72E, /*U+A72E*/ /**/
+  0xA72E, /*U+A72F*/ /*LATIN SMALL LETTER CUATRILLO WITH COMMA*/
+  0xA730, /*U+A730*/ /**/
+  0xA731, /*U+A731*/ /**/
+  0xA732, /*U+A732*/ /**/
+  0xA732, /*U+A733*/ /*LATIN SMALL LETTER AA*/
+  0xA734, /*U+A734*/ /**/
+  0xA734, /*U+A735*/ /*LATIN SMALL LETTER AO*/
+  0xA736, /*U+A736*/ /**/
+  0xA736, /*U+A737*/ /*LATIN SMALL LETTER AU*/
+  0xA738, /*U+A738*/ /**/
+  0xA738, /*U+A739*/ /*LATIN SMALL LETTER AV*/
+  0xA73A, /*U+A73A*/ /**/
+  0xA73A, /*U+A73B*/ /*LATIN SMALL LETTER AV WITH HORIZONTAL BAR*/
+  0xA73C, /*U+A73C*/ /**/
+  0xA73C, /*U+A73D*/ /*LATIN SMALL LETTER AY*/
+  0xA73E, /*U+A73E*/ /**/
+  0xA73E, /*U+A73F*/ /*LATIN SMALL LETTER REVERSED C WITH DOT*/
+  0xA740, /*U+A740*/ /**/
+  0xA740, /*U+A741*/ /*LATIN SMALL LETTER K WITH STROKE*/
+  0xA742, /*U+A742*/ /**/
+  0xA742, /*U+A743*/ /*LATIN SMALL LETTER K WITH DIAGONAL STROKE*/
+  0xA744, /*U+A744*/ /**/
+  0xA744, /*U+A745*/ /*LATIN SMALL LETTER K WITH STROKE AND DIAGONAL STROKE*/
+  0xA746, /*U+A746*/ /**/
+  0xA746, /*U+A747*/ /*LATIN SMALL LETTER BROKEN L*/
+  0xA748, /*U+A748*/ /**/
+  0xA748, /*U+A749*/ /*LATIN SMALL LETTER L WITH HIGH STROKE*/
+  0xA74A, /*U+A74A*/ /**/
+  0xA74A, /*U+A74B*/ /*LATIN SMALL LETTER O WITH LONG STROKE OVERLAY*/
+  0xA74C, /*U+A74C*/ /**/
+  0xA74C, /*U+A74D*/ /*LATIN SMALL LETTER O WITH LOOP*/
+  0xA74E, /*U+A74E*/ /**/
+  0xA74E, /*U+A74F*/ /*LATIN SMALL LETTER OO*/
+  0xA750, /*U+A750*/ /**/
+  0xA750, /*U+A751*/ /*LATIN SMALL LETTER P WITH STROKE THROUGH DESCENDER*/
+  0xA752, /*U+A752*/ /**/
+  0xA752, /*U+A753*/ /*LATIN SMALL LETTER P WITH FLOURISH*/
+  0xA754, /*U+A754*/ /**/
+  0xA754, /*U+A755*/ /*LATIN SMALL LETTER P WITH SQUIRREL TAIL*/
+  0xA756, /*U+A756*/ /**/
+  0xA756, /*U+A757*/ /*LATIN SMALL LETTER Q WITH STROKE THROUGH DESCENDER*/
+  0xA758, /*U+A758*/ /**/
+  0xA758, /*U+A759*/ /*LATIN SMALL LETTER Q WITH DIAGONAL STROKE*/
+  0xA75A, /*U+A75A*/ /**/
+  0xA75A, /*U+A75B*/ /*LATIN SMALL LETTER R ROTUNDA*/
+  0xA75C, /*U+A75C*/ /**/
+  0xA75C, /*U+A75D*/ /*LATIN SMALL LETTER RUM ROTUNDA*/
+  0xA75E, /*U+A75E*/ /**/
+  0xA75E, /*U+A75F*/ /*LATIN SMALL LETTER V WITH DIAGONAL STROKE*/
+  0xA760, /*U+A760*/ /**/
+  0xA760, /*U+A761*/ /*LATIN SMALL LETTER VY*/
+  0xA762, /*U+A762*/ /**/
+  0xA762, /*U+A763*/ /*LATIN SMALL LETTER VISIGOTHIC Z*/
+  0xA764, /*U+A764*/ /**/
+  0xA764, /*U+A765*/ /*LATIN SMALL LETTER THORN WITH STROKE*/
+  0xA766, /*U+A766*/ /**/
+  0xA766, /*U+A767*/ /*LATIN SMALL LETTER THORN WITH STROKE THROUGH DESCENDER*/
+  0xA768, /*U+A768*/ /**/
+  0xA768, /*U+A769*/ /*LATIN SMALL LETTER VEND*/
+  0xA76A, /*U+A76A*/ /**/
+  0xA76A, /*U+A76B*/ /*LATIN SMALL LETTER ET*/
+  0xA76C, /*U+A76C*/ /**/
+  0xA76C, /*U+A76D*/ /*LATIN SMALL LETTER IS*/
+  0xA76E, /*U+A76E*/ /**/
+  0xA76E, /*U+A76F*/ /*LATIN SMALL LETTER CON*/
+  0xA770, /*U+A770*/ /**/
+  0xA771, /*U+A771*/ /**/
+  0xA772, /*U+A772*/ /**/
+  0xA773, /*U+A773*/ /**/
+  0xA774, /*U+A774*/ /**/
+  0xA775, /*U+A775*/ /**/
+  0xA776, /*U+A776*/ /**/
+  0xA777, /*U+A777*/ /**/
+  0xA778, /*U+A778*/ /**/
+  0xA779, /*U+A779*/ /**/
+  0xA779, /*U+A77A*/ /*LATIN SMALL LETTER INSULAR D*/
+  0xA77B, /*U+A77B*/ /**/
+  0xA77B, /*U+A77C*/ /*LATIN SMALL LETTER INSULAR F*/
+  0xA77D, /*U+A77D*/ /**/
+  0xA77E, /*U+A77E*/ /**/
+  0xA77E, /*U+A77F*/ /*LATIN SMALL LETTER TURNED INSULAR G*/
+  0xA780, /*U+A780*/ /**/
+  0xA780, /*U+A781*/ /*LATIN SMALL LETTER TURNED L*/
+  0xA782, /*U+A782*/ /**/
+  0xA782, /*U+A783*/ /*LATIN SMALL LETTER INSULAR R*/
+  0xA784, /*U+A784*/ /**/
+  0xA784, /*U+A785*/ /*LATIN SMALL LETTER INSULAR S*/
+  0xA786, /*U+A786*/ /**/
+  0xA786, /*U+A787*/ /*LATIN SMALL LETTER INSULAR T*/
+  0xA788, /*U+A788*/ /**/
+  0xA789, /*U+A789*/ /**/
+  0xA78A, /*U+A78A*/ /**/
+  0xA78B, /*U+A78B*/ /**/
+  0xA78B, /*U+A78C*/ /*LATIN SMALL LETTER SALTILLO*/
+  0xA78D, /*U+A78D*/ /**/
+  0xA78E, /*U+A78E*/ /**/
+  0xA78F, /*U+A78F*/ /**/
+  0xA790, /*U+A790*/ /**/
+  0xA790, /*U+A791*/ /*LATIN SMALL LETTER N WITH DESCENDER*/
+  0xA792, /*U+A792*/ /**/
+  0xA793, /*U+A793*/ /**/
+  0xA794, /*U+A794*/ /**/
+  0xA795, /*U+A795*/ /**/
+  0xA796, /*U+A796*/ /**/
+  0xA797, /*U+A797*/ /**/
+  0xA798, /*U+A798*/ /**/
+  0xA799, /*U+A799*/ /**/
+  0xA79A, /*U+A79A*/ /**/
+  0xA79B, /*U+A79B*/ /**/
+  0xA79C, /*U+A79C*/ /**/
+  0xA79D, /*U+A79D*/ /**/
+  0xA79E, /*U+A79E*/ /**/
+  0xA79F, /*U+A79F*/ /**/
+  0xA7A0, /*U+A7A0*/ /**/
+  0xA7A0, /*U+A7A1*/ /*LATIN SMALL LETTER G WITH OBLIQUE STROKE*/
+  0xA7A2, /*U+A7A2*/ /**/
+  0xA7A2, /*U+A7A3*/ /*LATIN SMALL LETTER K WITH OBLIQUE STROKE*/
+  0xA7A4, /*U+A7A4*/ /**/
+  0xA7A4, /*U+A7A5*/ /*LATIN SMALL LETTER N WITH OBLIQUE STROKE*/
+  0xA7A6, /*U+A7A6*/ /**/
+  0xA7A6, /*U+A7A7*/ /*LATIN SMALL LETTER R WITH OBLIQUE STROKE*/
+  0xA7A8, /*U+A7A8*/ /**/
+  0xA7A8, /*U+A7A9*/ /*LATIN SMALL LETTER S WITH OBLIQUE STROKE*/
+  0xA7AA, /*U+A7AA*/ /**/
+  0xA7AB, /*U+A7AB*/ /**/
+  0xA7AC, /*U+A7AC*/ /**/
+  0xA7AD, /*U+A7AD*/ /**/
+  0xA7AE, /*U+A7AE*/ /**/
+  0xA7AF, /*U+A7AF*/ /**/
+  0xA7B0, /*U+A7B0*/ /**/
+  0xA7B1, /*U+A7B1*/ /**/
+  0xA7B2, /*U+A7B2*/ /**/
+  0xA7B3, /*U+A7B3*/ /**/
+  0xA7B4, /*U+A7B4*/ /**/
+  0xA7B5, /*U+A7B5*/ /**/
+  0xA7B6, /*U+A7B6*/ /**/
+  0xA7B7, /*U+A7B7*/ /**/
+  0xA7B8, /*U+A7B8*/ /**/
+  0xA7B9, /*U+A7B9*/ /**/
+  0xA7BA, /*U+A7BA*/ /**/
+  0xA7BB, /*U+A7BB*/ /**/
+  0xA7BC, /*U+A7BC*/ /**/
+  0xA7BD, /*U+A7BD*/ /**/
+  0xA7BE, /*U+A7BE*/ /**/
+  0xA7BF, /*U+A7BF*/ /**/
+};
+
+static const u_int16_t upper_table_10[64] = {
+  0xFF40, /*U+FF40*/ /**/
+  0xFF21, /*U+FF41*/ /*FULLWIDTH LATIN SMALL LETTER A*/
+  0xFF22, /*U+FF42*/ /*FULLWIDTH LATIN SMALL LETTER B*/
+  0xFF23, /*U+FF43*/ /*FULLWIDTH LATIN SMALL LETTER C*/
+  0xFF24, /*U+FF44*/ /*FULLWIDTH LATIN SMALL LETTER D*/
+  0xFF25, /*U+FF45*/ /*FULLWIDTH LATIN SMALL LETTER E*/
+  0xFF26, /*U+FF46*/ /*FULLWIDTH LATIN SMALL LETTER F*/
+  0xFF27, /*U+FF47*/ /*FULLWIDTH LATIN SMALL LETTER G*/
+  0xFF28, /*U+FF48*/ /*FULLWIDTH LATIN SMALL LETTER H*/
+  0xFF29, /*U+FF49*/ /*FULLWIDTH LATIN SMALL LETTER I*/
+  0xFF2A, /*U+FF4A*/ /*FULLWIDTH LATIN SMALL LETTER J*/
+  0xFF2B, /*U+FF4B*/ /*FULLWIDTH LATIN SMALL LETTER K*/
+  0xFF2C, /*U+FF4C*/ /*FULLWIDTH LATIN SMALL LETTER L*/
+  0xFF2D, /*U+FF4D*/ /*FULLWIDTH LATIN SMALL LETTER M*/
+  0xFF2E, /*U+FF4E*/ /*FULLWIDTH LATIN SMALL LETTER N*/
+  0xFF2F, /*U+FF4F*/ /*FULLWIDTH LATIN SMALL LETTER O*/
+  0xFF30, /*U+FF50*/ /*FULLWIDTH LATIN SMALL LETTER P*/
+  0xFF31, /*U+FF51*/ /*FULLWIDTH LATIN SMALL LETTER Q*/
+  0xFF32, /*U+FF52*/ /*FULLWIDTH LATIN SMALL LETTER R*/
+  0xFF33, /*U+FF53*/ /*FULLWIDTH LATIN SMALL LETTER S*/
+  0xFF34, /*U+FF54*/ /*FULLWIDTH LATIN SMALL LETTER T*/
+  0xFF35, /*U+FF55*/ /*FULLWIDTH LATIN SMALL LETTER U*/
+  0xFF36, /*U+FF56*/ /*FULLWIDTH LATIN SMALL LETTER V*/
+  0xFF37, /*U+FF57*/ /*FULLWIDTH LATIN SMALL LETTER W*/
+  0xFF38, /*U+FF58*/ /*FULLWIDTH LATIN SMALL LETTER X*/
+  0xFF39, /*U+FF59*/ /*FULLWIDTH LATIN SMALL LETTER Y*/
+  0xFF3A, /*U+FF5A*/ /*FULLWIDTH LATIN SMALL LETTER Z*/
+  0xFF5B, /*U+FF5B*/ /**/
+  0xFF5C, /*U+FF5C*/ /**/
+  0xFF5D, /*U+FF5D*/ /**/
+  0xFF5E, /*U+FF5E*/ /**/
+  0xFF5F, /*U+FF5F*/ /**/
+  0xFF60, /*U+FF60*/ /**/
+  0xFF61, /*U+FF61*/ /**/
+  0xFF62, /*U+FF62*/ /**/
+  0xFF63, /*U+FF63*/ /**/
+  0xFF64, /*U+FF64*/ /**/
+  0xFF65, /*U+FF65*/ /**/
+  0xFF66, /*U+FF66*/ /**/
+  0xFF67, /*U+FF67*/ /**/
+  0xFF68, /*U+FF68*/ /**/
+  0xFF69, /*U+FF69*/ /**/
+  0xFF6A, /*U+FF6A*/ /**/
+  0xFF6B, /*U+FF6B*/ /**/
+  0xFF6C, /*U+FF6C*/ /**/
+  0xFF6D, /*U+FF6D*/ /**/
+  0xFF6E, /*U+FF6E*/ /**/
+  0xFF6F, /*U+FF6F*/ /**/
+  0xFF70, /*U+FF70*/ /**/
+  0xFF71, /*U+FF71*/ /**/
+  0xFF72, /*U+FF72*/ /**/
+  0xFF73, /*U+FF73*/ /**/
+  0xFF74, /*U+FF74*/ /**/
+  0xFF75, /*U+FF75*/ /**/
+  0xFF76, /*U+FF76*/ /**/
+  0xFF77, /*U+FF77*/ /**/
+  0xFF78, /*U+FF78*/ /**/
+  0xFF79, /*U+FF79*/ /**/
+  0xFF7A, /*U+FF7A*/ /**/
+  0xFF7B, /*U+FF7B*/ /**/
+  0xFF7C, /*U+FF7C*/ /**/
+  0xFF7D, /*U+FF7D*/ /**/
+  0xFF7E, /*U+FF7E*/ /**/
+  0xFF7F, /*U+FF7F*/ /**/
+};
+
+static const u_int32_t upper_table_sp_1[128] = {
+  0xD801DC00, /*0xD801DC00*/ /*U+010400*/ /*U+010400*/ /**/
+  0xD801DC01, /*0xD801DC01*/ /*U+010401*/ /*U+010401*/ /**/
+  0xD801DC02, /*0xD801DC02*/ /*U+010402*/ /*U+010402*/ /**/
+  0xD801DC03, /*0xD801DC03*/ /*U+010403*/ /*U+010403*/ /**/
+  0xD801DC04, /*0xD801DC04*/ /*U+010404*/ /*U+010404*/ /**/
+  0xD801DC05, /*0xD801DC05*/ /*U+010405*/ /*U+010405*/ /**/
+  0xD801DC06, /*0xD801DC06*/ /*U+010406*/ /*U+010406*/ /**/
+  0xD801DC07, /*0xD801DC07*/ /*U+010407*/ /*U+010407*/ /**/
+  0xD801DC08, /*0xD801DC08*/ /*U+010408*/ /*U+010408*/ /**/
+  0xD801DC09, /*0xD801DC09*/ /*U+010409*/ /*U+010409*/ /**/
+  0xD801DC0A, /*0xD801DC0A*/ /*U+01040A*/ /*U+01040A*/ /**/
+  0xD801DC0B, /*0xD801DC0B*/ /*U+01040B*/ /*U+01040B*/ /**/
+  0xD801DC0C, /*0xD801DC0C*/ /*U+01040C*/ /*U+01040C*/ /**/
+  0xD801DC0D, /*0xD801DC0D*/ /*U+01040D*/ /*U+01040D*/ /**/
+  0xD801DC0E, /*0xD801DC0E*/ /*U+01040E*/ /*U+01040E*/ /**/
+  0xD801DC0F, /*0xD801DC0F*/ /*U+01040F*/ /*U+01040F*/ /**/
+  0xD801DC10, /*0xD801DC10*/ /*U+010410*/ /*U+010410*/ /**/
+  0xD801DC11, /*0xD801DC11*/ /*U+010411*/ /*U+010411*/ /**/
+  0xD801DC12, /*0xD801DC12*/ /*U+010412*/ /*U+010412*/ /**/
+  0xD801DC13, /*0xD801DC13*/ /*U+010413*/ /*U+010413*/ /**/
+  0xD801DC14, /*0xD801DC14*/ /*U+010414*/ /*U+010414*/ /**/
+  0xD801DC15, /*0xD801DC15*/ /*U+010415*/ /*U+010415*/ /**/
+  0xD801DC16, /*0xD801DC16*/ /*U+010416*/ /*U+010416*/ /**/
+  0xD801DC17, /*0xD801DC17*/ /*U+010417*/ /*U+010417*/ /**/
+  0xD801DC18, /*0xD801DC18*/ /*U+010418*/ /*U+010418*/ /**/
+  0xD801DC19, /*0xD801DC19*/ /*U+010419*/ /*U+010419*/ /**/
+  0xD801DC1A, /*0xD801DC1A*/ /*U+01041A*/ /*U+01041A*/ /**/
+  0xD801DC1B, /*0xD801DC1B*/ /*U+01041B*/ /*U+01041B*/ /**/
+  0xD801DC1C, /*0xD801DC1C*/ /*U+01041C*/ /*U+01041C*/ /**/
+  0xD801DC1D, /*0xD801DC1D*/ /*U+01041D*/ /*U+01041D*/ /**/
+  0xD801DC1E, /*0xD801DC1E*/ /*U+01041E*/ /*U+01041E*/ /**/
+  0xD801DC1F, /*0xD801DC1F*/ /*U+01041F*/ /*U+01041F*/ /**/
+  0xD801DC20, /*0xD801DC20*/ /*U+010420*/ /*U+010420*/ /**/
+  0xD801DC21, /*0xD801DC21*/ /*U+010421*/ /*U+010421*/ /**/
+  0xD801DC22, /*0xD801DC22*/ /*U+010422*/ /*U+010422*/ /**/
+  0xD801DC23, /*0xD801DC23*/ /*U+010423*/ /*U+010423*/ /**/
+  0xD801DC24, /*0xD801DC24*/ /*U+010424*/ /*U+010424*/ /**/
+  0xD801DC25, /*0xD801DC25*/ /*U+010425*/ /*U+010425*/ /**/
+  0xD801DC26, /*0xD801DC26*/ /*U+010426*/ /*U+010426*/ /**/
+  0xD801DC27, /*0xD801DC27*/ /*U+010427*/ /*U+010427*/ /**/
+  0xD801DC00, /*0xD801DC28*/ /*U+010400*/ /*U+010428*/ /*DESERET SMALL LETTER LONG I*/
+  0xD801DC01, /*0xD801DC29*/ /*U+010401*/ /*U+010429*/ /*DESERET SMALL LETTER LONG E*/
+  0xD801DC02, /*0xD801DC2A*/ /*U+010402*/ /*U+01042A*/ /*DESERET SMALL LETTER LONG A*/
+  0xD801DC03, /*0xD801DC2B*/ /*U+010403*/ /*U+01042B*/ /*DESERET SMALL LETTER LONG AH*/
+  0xD801DC04, /*0xD801DC2C*/ /*U+010404*/ /*U+01042C*/ /*DESERET SMALL LETTER LONG O*/
+  0xD801DC05, /*0xD801DC2D*/ /*U+010405*/ /*U+01042D*/ /*DESERET SMALL LETTER LONG OO*/
+  0xD801DC06, /*0xD801DC2E*/ /*U+010406*/ /*U+01042E*/ /*DESERET SMALL LETTER SHORT I*/
+  0xD801DC07, /*0xD801DC2F*/ /*U+010407*/ /*U+01042F*/ /*DESERET SMALL LETTER SHORT E*/
+  0xD801DC08, /*0xD801DC30*/ /*U+010408*/ /*U+010430*/ /*DESERET SMALL LETTER SHORT A*/
+  0xD801DC09, /*0xD801DC31*/ /*U+010409*/ /*U+010431*/ /*DESERET SMALL LETTER SHORT AH*/
+  0xD801DC0A, /*0xD801DC32*/ /*U+01040A*/ /*U+010432*/ /*DESERET SMALL LETTER SHORT O*/
+  0xD801DC0B, /*0xD801DC33*/ /*U+01040B*/ /*U+010433*/ /*DESERET SMALL LETTER SHORT OO*/
+  0xD801DC0C, /*0xD801DC34*/ /*U+01040C*/ /*U+010434*/ /*DESERET SMALL LETTER AY*/
+  0xD801DC0D, /*0xD801DC35*/ /*U+01040D*/ /*U+010435*/ /*DESERET SMALL LETTER OW*/
+  0xD801DC0E, /*0xD801DC36*/ /*U+01040E*/ /*U+010436*/ /*DESERET SMALL LETTER WU*/
+  0xD801DC0F, /*0xD801DC37*/ /*U+01040F*/ /*U+010437*/ /*DESERET SMALL LETTER YEE*/
+  0xD801DC10, /*0xD801DC38*/ /*U+010410*/ /*U+010438*/ /*DESERET SMALL LETTER H*/
+  0xD801DC11, /*0xD801DC39*/ /*U+010411*/ /*U+010439*/ /*DESERET SMALL LETTER PEE*/
+  0xD801DC12, /*0xD801DC3A*/ /*U+010412*/ /*U+01043A*/ /*DESERET SMALL LETTER BEE*/
+  0xD801DC13, /*0xD801DC3B*/ /*U+010413*/ /*U+01043B*/ /*DESERET SMALL LETTER TEE*/
+  0xD801DC14, /*0xD801DC3C*/ /*U+010414*/ /*U+01043C*/ /*DESERET SMALL LETTER DEE*/
+  0xD801DC15, /*0xD801DC3D*/ /*U+010415*/ /*U+01043D*/ /*DESERET SMALL LETTER CHEE*/
+  0xD801DC16, /*0xD801DC3E*/ /*U+010416*/ /*U+01043E*/ /*DESERET SMALL LETTER JEE*/
+  0xD801DC17, /*0xD801DC3F*/ /*U+010417*/ /*U+01043F*/ /*DESERET SMALL LETTER KAY*/
+  0xD801DC18, /*0xD801DC40*/ /*U+010418*/ /*U+010440*/ /*DESERET SMALL LETTER GAY*/
+  0xD801DC19, /*0xD801DC41*/ /*U+010419*/ /*U+010441*/ /*DESERET SMALL LETTER EF*/
+  0xD801DC1A, /*0xD801DC42*/ /*U+01041A*/ /*U+010442*/ /*DESERET SMALL LETTER VEE*/
+  0xD801DC1B, /*0xD801DC43*/ /*U+01041B*/ /*U+010443*/ /*DESERET SMALL LETTER ETH*/
+  0xD801DC1C, /*0xD801DC44*/ /*U+01041C*/ /*U+010444*/ /*DESERET SMALL LETTER THEE*/
+  0xD801DC1D, /*0xD801DC45*/ /*U+01041D*/ /*U+010445*/ /*DESERET SMALL LETTER ES*/
+  0xD801DC1E, /*0xD801DC46*/ /*U+01041E*/ /*U+010446*/ /*DESERET SMALL LETTER ZEE*/
+  0xD801DC1F, /*0xD801DC47*/ /*U+01041F*/ /*U+010447*/ /*DESERET SMALL LETTER ESH*/
+  0xD801DC20, /*0xD801DC48*/ /*U+010420*/ /*U+010448*/ /*DESERET SMALL LETTER ZHEE*/
+  0xD801DC21, /*0xD801DC49*/ /*U+010421*/ /*U+010449*/ /*DESERET SMALL LETTER ER*/
+  0xD801DC22, /*0xD801DC4A*/ /*U+010422*/ /*U+01044A*/ /*DESERET SMALL LETTER EL*/
+  0xD801DC23, /*0xD801DC4B*/ /*U+010423*/ /*U+01044B*/ /*DESERET SMALL LETTER EM*/
+  0xD801DC24, /*0xD801DC4C*/ /*U+010424*/ /*U+01044C*/ /*DESERET SMALL LETTER EN*/
+  0xD801DC25, /*0xD801DC4D*/ /*U+010425*/ /*U+01044D*/ /*DESERET SMALL LETTER ENG*/
+  0xD801DC26, /*0xD801DC4E*/ /*U+010426*/ /*U+01044E*/ /*DESERET SMALL LETTER OI*/
+  0xD801DC27, /*0xD801DC4F*/ /*U+010427*/ /*U+01044F*/ /*DESERET SMALL LETTER EW*/
+  0xD801DC50, /*0xD801DC50*/ /*U+010450*/ /*U+010450*/ /**/
+  0xD801DC51, /*0xD801DC51*/ /*U+010451*/ /*U+010451*/ /**/
+  0xD801DC52, /*0xD801DC52*/ /*U+010452*/ /*U+010452*/ /**/
+  0xD801DC53, /*0xD801DC53*/ /*U+010453*/ /*U+010453*/ /**/
+  0xD801DC54, /*0xD801DC54*/ /*U+010454*/ /*U+010454*/ /**/
+  0xD801DC55, /*0xD801DC55*/ /*U+010455*/ /*U+010455*/ /**/
+  0xD801DC56, /*0xD801DC56*/ /*U+010456*/ /*U+010456*/ /**/
+  0xD801DC57, /*0xD801DC57*/ /*U+010457*/ /*U+010457*/ /**/
+  0xD801DC58, /*0xD801DC58*/ /*U+010458*/ /*U+010458*/ /**/
+  0xD801DC59, /*0xD801DC59*/ /*U+010459*/ /*U+010459*/ /**/
+  0xD801DC5A, /*0xD801DC5A*/ /*U+01045A*/ /*U+01045A*/ /**/
+  0xD801DC5B, /*0xD801DC5B*/ /*U+01045B*/ /*U+01045B*/ /**/
+  0xD801DC5C, /*0xD801DC5C*/ /*U+01045C*/ /*U+01045C*/ /**/
+  0xD801DC5D, /*0xD801DC5D*/ /*U+01045D*/ /*U+01045D*/ /**/
+  0xD801DC5E, /*0xD801DC5E*/ /*U+01045E*/ /*U+01045E*/ /**/
+  0xD801DC5F, /*0xD801DC5F*/ /*U+01045F*/ /*U+01045F*/ /**/
+  0xD801DC60, /*0xD801DC60*/ /*U+010460*/ /*U+010460*/ /**/
+  0xD801DC61, /*0xD801DC61*/ /*U+010461*/ /*U+010461*/ /**/
+  0xD801DC62, /*0xD801DC62*/ /*U+010462*/ /*U+010462*/ /**/
+  0xD801DC63, /*0xD801DC63*/ /*U+010463*/ /*U+010463*/ /**/
+  0xD801DC64, /*0xD801DC64*/ /*U+010464*/ /*U+010464*/ /**/
+  0xD801DC65, /*0xD801DC65*/ /*U+010465*/ /*U+010465*/ /**/
+  0xD801DC66, /*0xD801DC66*/ /*U+010466*/ /*U+010466*/ /**/
+  0xD801DC67, /*0xD801DC67*/ /*U+010467*/ /*U+010467*/ /**/
+  0xD801DC68, /*0xD801DC68*/ /*U+010468*/ /*U+010468*/ /**/
+  0xD801DC69, /*0xD801DC69*/ /*U+010469*/ /*U+010469*/ /**/
+  0xD801DC6A, /*0xD801DC6A*/ /*U+01046A*/ /*U+01046A*/ /**/
+  0xD801DC6B, /*0xD801DC6B*/ /*U+01046B*/ /*U+01046B*/ /**/
+  0xD801DC6C, /*0xD801DC6C*/ /*U+01046C*/ /*U+01046C*/ /**/
+  0xD801DC6D, /*0xD801DC6D*/ /*U+01046D*/ /*U+01046D*/ /**/
+  0xD801DC6E, /*0xD801DC6E*/ /*U+01046E*/ /*U+01046E*/ /**/
+  0xD801DC6F, /*0xD801DC6F*/ /*U+01046F*/ /*U+01046F*/ /**/
+  0xD801DC70, /*0xD801DC70*/ /*U+010470*/ /*U+010470*/ /**/
+  0xD801DC71, /*0xD801DC71*/ /*U+010471*/ /*U+010471*/ /**/
+  0xD801DC72, /*0xD801DC72*/ /*U+010472*/ /*U+010472*/ /**/
+  0xD801DC73, /*0xD801DC73*/ /*U+010473*/ /*U+010473*/ /**/
+  0xD801DC74, /*0xD801DC74*/ /*U+010474*/ /*U+010474*/ /**/
+  0xD801DC75, /*0xD801DC75*/ /*U+010475*/ /*U+010475*/ /**/
+  0xD801DC76, /*0xD801DC76*/ /*U+010476*/ /*U+010476*/ /**/
+  0xD801DC77, /*0xD801DC77*/ /*U+010477*/ /*U+010477*/ /**/
+  0xD801DC78, /*0xD801DC78*/ /*U+010478*/ /*U+010478*/ /**/
+  0xD801DC79, /*0xD801DC79*/ /*U+010479*/ /*U+010479*/ /**/
+  0xD801DC7A, /*0xD801DC7A*/ /*U+01047A*/ /*U+01047A*/ /**/
+  0xD801DC7B, /*0xD801DC7B*/ /*U+01047B*/ /*U+01047B*/ /**/
+  0xD801DC7C, /*0xD801DC7C*/ /*U+01047C*/ /*U+01047C*/ /**/
+  0xD801DC7D, /*0xD801DC7D*/ /*U+01047D*/ /*U+01047D*/ /**/
+  0xD801DC7E, /*0xD801DC7E*/ /*U+01047E*/ /*U+01047E*/ /**/
+  0xD801DC7F, /*0xD801DC7F*/ /*U+01047F*/ /*U+01047F*/ /**/
+};
+
+static const u_int16_t lower_table_1[128] = {
+  0x0000, /*U+0000*/ /**/
+  0x0001, /*U+0001*/ /**/
+  0x0002, /*U+0002*/ /**/
+  0x0003, /*U+0003*/ /**/
+  0x0004, /*U+0004*/ /**/
+  0x0005, /*U+0005*/ /**/
+  0x0006, /*U+0006*/ /**/
+  0x0007, /*U+0007*/ /**/
+  0x0008, /*U+0008*/ /**/
+  0x0009, /*U+0009*/ /**/
+  0x000A, /*U+000A*/ /**/
+  0x000B, /*U+000B*/ /**/
+  0x000C, /*U+000C*/ /**/
+  0x000D, /*U+000D*/ /**/
+  0x000E, /*U+000E*/ /**/
+  0x000F, /*U+000F*/ /**/
+  0x0010, /*U+0010*/ /**/
+  0x0011, /*U+0011*/ /**/
+  0x0012, /*U+0012*/ /**/
+  0x0013, /*U+0013*/ /**/
+  0x0014, /*U+0014*/ /**/
+  0x0015, /*U+0015*/ /**/
+  0x0016, /*U+0016*/ /**/
+  0x0017, /*U+0017*/ /**/
+  0x0018, /*U+0018*/ /**/
+  0x0019, /*U+0019*/ /**/
+  0x001A, /*U+001A*/ /**/
+  0x001B, /*U+001B*/ /**/
+  0x001C, /*U+001C*/ /**/
+  0x001D, /*U+001D*/ /**/
+  0x001E, /*U+001E*/ /**/
+  0x001F, /*U+001F*/ /**/
+  0x0020, /*U+0020*/ /**/
+  0x0021, /*U+0021*/ /**/
+  0x0022, /*U+0022*/ /**/
+  0x0023, /*U+0023*/ /**/
+  0x0024, /*U+0024*/ /**/
+  0x0025, /*U+0025*/ /**/
+  0x0026, /*U+0026*/ /**/
+  0x0027, /*U+0027*/ /**/
+  0x0028, /*U+0028*/ /**/
+  0x0029, /*U+0029*/ /**/
+  0x002A, /*U+002A*/ /**/
+  0x002B, /*U+002B*/ /**/
+  0x002C, /*U+002C*/ /**/
+  0x002D, /*U+002D*/ /**/
+  0x002E, /*U+002E*/ /**/
+  0x002F, /*U+002F*/ /**/
+  0x0030, /*U+0030*/ /**/
+  0x0031, /*U+0031*/ /**/
+  0x0032, /*U+0032*/ /**/
+  0x0033, /*U+0033*/ /**/
+  0x0034, /*U+0034*/ /**/
+  0x0035, /*U+0035*/ /**/
+  0x0036, /*U+0036*/ /**/
+  0x0037, /*U+0037*/ /**/
+  0x0038, /*U+0038*/ /**/
+  0x0039, /*U+0039*/ /**/
+  0x003A, /*U+003A*/ /**/
+  0x003B, /*U+003B*/ /**/
+  0x003C, /*U+003C*/ /**/
+  0x003D, /*U+003D*/ /**/
+  0x003E, /*U+003E*/ /**/
+  0x003F, /*U+003F*/ /**/
+  0x0040, /*U+0040*/ /**/
+  0x0061, /*U+0041*/ /*LATIN CAPITAL LETTER A*/
+  0x0062, /*U+0042*/ /*LATIN CAPITAL LETTER B*/
+  0x0063, /*U+0043*/ /*LATIN CAPITAL LETTER C*/
+  0x0064, /*U+0044*/ /*LATIN CAPITAL LETTER D*/
+  0x0065, /*U+0045*/ /*LATIN CAPITAL LETTER E*/
+  0x0066, /*U+0046*/ /*LATIN CAPITAL LETTER F*/
+  0x0067, /*U+0047*/ /*LATIN CAPITAL LETTER G*/
+  0x0068, /*U+0048*/ /*LATIN CAPITAL LETTER H*/
+  0x0069, /*U+0049*/ /*LATIN CAPITAL LETTER I*/
+  0x006A, /*U+004A*/ /*LATIN CAPITAL LETTER J*/
+  0x006B, /*U+004B*/ /*LATIN CAPITAL LETTER K*/
+  0x006C, /*U+004C*/ /*LATIN CAPITAL LETTER L*/
+  0x006D, /*U+004D*/ /*LATIN CAPITAL LETTER M*/
+  0x006E, /*U+004E*/ /*LATIN CAPITAL LETTER N*/
+  0x006F, /*U+004F*/ /*LATIN CAPITAL LETTER O*/
+  0x0070, /*U+0050*/ /*LATIN CAPITAL LETTER P*/
+  0x0071, /*U+0051*/ /*LATIN CAPITAL LETTER Q*/
+  0x0072, /*U+0052*/ /*LATIN CAPITAL LETTER R*/
+  0x0073, /*U+0053*/ /*LATIN CAPITAL LETTER S*/
+  0x0074, /*U+0054*/ /*LATIN CAPITAL LETTER T*/
+  0x0075, /*U+0055*/ /*LATIN CAPITAL LETTER U*/
+  0x0076, /*U+0056*/ /*LATIN CAPITAL LETTER V*/
+  0x0077, /*U+0057*/ /*LATIN CAPITAL LETTER W*/
+  0x0078, /*U+0058*/ /*LATIN CAPITAL LETTER X*/
+  0x0079, /*U+0059*/ /*LATIN CAPITAL LETTER Y*/
+  0x007A, /*U+005A*/ /*LATIN CAPITAL LETTER Z*/
+  0x005B, /*U+005B*/ /**/
+  0x005C, /*U+005C*/ /**/
+  0x005D, /*U+005D*/ /**/
+  0x005E, /*U+005E*/ /**/
+  0x005F, /*U+005F*/ /**/
+  0x0060, /*U+0060*/ /**/
+  0x0061, /*U+0061*/ /**/
+  0x0062, /*U+0062*/ /**/
+  0x0063, /*U+0063*/ /**/
+  0x0064, /*U+0064*/ /**/
+  0x0065, /*U+0065*/ /**/
+  0x0066, /*U+0066*/ /**/
+  0x0067, /*U+0067*/ /**/
+  0x0068, /*U+0068*/ /**/
+  0x0069, /*U+0069*/ /**/
+  0x006A, /*U+006A*/ /**/
+  0x006B, /*U+006B*/ /**/
+  0x006C, /*U+006C*/ /**/
+  0x006D, /*U+006D*/ /**/
+  0x006E, /*U+006E*/ /**/
+  0x006F, /*U+006F*/ /**/
+  0x0070, /*U+0070*/ /**/
+  0x0071, /*U+0071*/ /**/
+  0x0072, /*U+0072*/ /**/
+  0x0073, /*U+0073*/ /**/
+  0x0074, /*U+0074*/ /**/
+  0x0075, /*U+0075*/ /**/
+  0x0076, /*U+0076*/ /**/
+  0x0077, /*U+0077*/ /**/
+  0x0078, /*U+0078*/ /**/
+  0x0079, /*U+0079*/ /**/
+  0x007A, /*U+007A*/ /**/
+  0x007B, /*U+007B*/ /**/
+  0x007C, /*U+007C*/ /**/
+  0x007D, /*U+007D*/ /**/
+  0x007E, /*U+007E*/ /**/
+  0x007F, /*U+007F*/ /**/
+};
+
+static const u_int16_t lower_table_2[448] = {
+  0x00E0, /*U+00C0*/ /*LATIN CAPITAL LETTER A WITH GRAVE*/
+  0x00E1, /*U+00C1*/ /*LATIN CAPITAL LETTER A WITH ACUTE*/
+  0x00E2, /*U+00C2*/ /*LATIN CAPITAL LETTER A WITH CIRCUMFLEX*/
+  0x00E3, /*U+00C3*/ /*LATIN CAPITAL LETTER A WITH TILDE*/
+  0x00E4, /*U+00C4*/ /*LATIN CAPITAL LETTER A WITH DIAERESIS*/
+  0x00E5, /*U+00C5*/ /*LATIN CAPITAL LETTER A WITH RING ABOVE*/
+  0x00E6, /*U+00C6*/ /*LATIN CAPITAL LETTER AE*/
+  0x00E7, /*U+00C7*/ /*LATIN CAPITAL LETTER C WITH CEDILLA*/
+  0x00E8, /*U+00C8*/ /*LATIN CAPITAL LETTER E WITH GRAVE*/
+  0x00E9, /*U+00C9*/ /*LATIN CAPITAL LETTER E WITH ACUTE*/
+  0x00EA, /*U+00CA*/ /*LATIN CAPITAL LETTER E WITH CIRCUMFLEX*/
+  0x00EB, /*U+00CB*/ /*LATIN CAPITAL LETTER E WITH DIAERESIS*/
+  0x00EC, /*U+00CC*/ /*LATIN CAPITAL LETTER I WITH GRAVE*/
+  0x00ED, /*U+00CD*/ /*LATIN CAPITAL LETTER I WITH ACUTE*/
+  0x00EE, /*U+00CE*/ /*LATIN CAPITAL LETTER I WITH CIRCUMFLEX*/
+  0x00EF, /*U+00CF*/ /*LATIN CAPITAL LETTER I WITH DIAERESIS*/
+  0x00F0, /*U+00D0*/ /*LATIN CAPITAL LETTER ETH*/
+  0x00F1, /*U+00D1*/ /*LATIN CAPITAL LETTER N WITH TILDE*/
+  0x00F2, /*U+00D2*/ /*LATIN CAPITAL LETTER O WITH GRAVE*/
+  0x00F3, /*U+00D3*/ /*LATIN CAPITAL LETTER O WITH ACUTE*/
+  0x00F4, /*U+00D4*/ /*LATIN CAPITAL LETTER O WITH CIRCUMFLEX*/
+  0x00F5, /*U+00D5*/ /*LATIN CAPITAL LETTER O WITH TILDE*/
+  0x00F6, /*U+00D6*/ /*LATIN CAPITAL LETTER O WITH DIAERESIS*/
+  0x00D7, /*U+00D7*/ /**/
+  0x00F8, /*U+00D8*/ /*LATIN CAPITAL LETTER O WITH STROKE*/
+  0x00F9, /*U+00D9*/ /*LATIN CAPITAL LETTER U WITH GRAVE*/
+  0x00FA, /*U+00DA*/ /*LATIN CAPITAL LETTER U WITH ACUTE*/
+  0x00FB, /*U+00DB*/ /*LATIN CAPITAL LETTER U WITH CIRCUMFLEX*/
+  0x00FC, /*U+00DC*/ /*LATIN CAPITAL LETTER U WITH DIAERESIS*/
+  0x00FD, /*U+00DD*/ /*LATIN CAPITAL LETTER Y WITH ACUTE*/
+  0x00FE, /*U+00DE*/ /*LATIN CAPITAL LETTER THORN*/
+  0x00DF, /*U+00DF*/ /**/
+  0x00E0, /*U+00E0*/ /**/
+  0x00E1, /*U+00E1*/ /**/
+  0x00E2, /*U+00E2*/ /**/
+  0x00E3, /*U+00E3*/ /**/
+  0x00E4, /*U+00E4*/ /**/
+  0x00E5, /*U+00E5*/ /**/
+  0x00E6, /*U+00E6*/ /**/
+  0x00E7, /*U+00E7*/ /**/
+  0x00E8, /*U+00E8*/ /**/
+  0x00E9, /*U+00E9*/ /**/
+  0x00EA, /*U+00EA*/ /**/
+  0x00EB, /*U+00EB*/ /**/
+  0x00EC, /*U+00EC*/ /**/
+  0x00ED, /*U+00ED*/ /**/
+  0x00EE, /*U+00EE*/ /**/
+  0x00EF, /*U+00EF*/ /**/
+  0x00F0, /*U+00F0*/ /**/
+  0x00F1, /*U+00F1*/ /**/
+  0x00F2, /*U+00F2*/ /**/
+  0x00F3, /*U+00F3*/ /**/
+  0x00F4, /*U+00F4*/ /**/
+  0x00F5, /*U+00F5*/ /**/
+  0x00F6, /*U+00F6*/ /**/
+  0x00F7, /*U+00F7*/ /**/
+  0x00F8, /*U+00F8*/ /**/
+  0x00F9, /*U+00F9*/ /**/
+  0x00FA, /*U+00FA*/ /**/
+  0x00FB, /*U+00FB*/ /**/
+  0x00FC, /*U+00FC*/ /**/
+  0x00FD, /*U+00FD*/ /**/
+  0x00FE, /*U+00FE*/ /**/
+  0x00FF, /*U+00FF*/ /**/
+  0x0101, /*U+0100*/ /*LATIN CAPITAL LETTER A WITH MACRON*/
+  0x0101, /*U+0101*/ /**/
+  0x0103, /*U+0102*/ /*LATIN CAPITAL LETTER A WITH BREVE*/
+  0x0103, /*U+0103*/ /**/
+  0x0105, /*U+0104*/ /*LATIN CAPITAL LETTER A WITH OGONEK*/
+  0x0105, /*U+0105*/ /**/
+  0x0107, /*U+0106*/ /*LATIN CAPITAL LETTER C WITH ACUTE*/
+  0x0107, /*U+0107*/ /**/
+  0x0109, /*U+0108*/ /*LATIN CAPITAL LETTER C WITH CIRCUMFLEX*/
+  0x0109, /*U+0109*/ /**/
+  0x010B, /*U+010A*/ /*LATIN CAPITAL LETTER C WITH DOT ABOVE*/
+  0x010B, /*U+010B*/ /**/
+  0x010D, /*U+010C*/ /*LATIN CAPITAL LETTER C WITH CARON*/
+  0x010D, /*U+010D*/ /**/
+  0x010F, /*U+010E*/ /*LATIN CAPITAL LETTER D WITH CARON*/
+  0x010F, /*U+010F*/ /**/
+  0x0111, /*U+0110*/ /*LATIN CAPITAL LETTER D WITH STROKE*/
+  0x0111, /*U+0111*/ /**/
+  0x0113, /*U+0112*/ /*LATIN CAPITAL LETTER E WITH MACRON*/
+  0x0113, /*U+0113*/ /**/
+  0x0115, /*U+0114*/ /*LATIN CAPITAL LETTER E WITH BREVE*/
+  0x0115, /*U+0115*/ /**/
+  0x0117, /*U+0116*/ /*LATIN CAPITAL LETTER E WITH DOT ABOVE*/
+  0x0117, /*U+0117*/ /**/
+  0x0119, /*U+0118*/ /*LATIN CAPITAL LETTER E WITH OGONEK*/
+  0x0119, /*U+0119*/ /**/
+  0x011B, /*U+011A*/ /*LATIN CAPITAL LETTER E WITH CARON*/
+  0x011B, /*U+011B*/ /**/
+  0x011D, /*U+011C*/ /*LATIN CAPITAL LETTER G WITH CIRCUMFLEX*/
+  0x011D, /*U+011D*/ /**/
+  0x011F, /*U+011E*/ /*LATIN CAPITAL LETTER G WITH BREVE*/
+  0x011F, /*U+011F*/ /**/
+  0x0121, /*U+0120*/ /*LATIN CAPITAL LETTER G WITH DOT ABOVE*/
+  0x0121, /*U+0121*/ /**/
+  0x0123, /*U+0122*/ /*LATIN CAPITAL LETTER G WITH CEDILLA*/
+  0x0123, /*U+0123*/ /**/
+  0x0125, /*U+0124*/ /*LATIN CAPITAL LETTER H WITH CIRCUMFLEX*/
+  0x0125, /*U+0125*/ /**/
+  0x0127, /*U+0126*/ /*LATIN CAPITAL LETTER H WITH STROKE*/
+  0x0127, /*U+0127*/ /**/
+  0x0129, /*U+0128*/ /*LATIN CAPITAL LETTER I WITH TILDE*/
+  0x0129, /*U+0129*/ /**/
+  0x012B, /*U+012A*/ /*LATIN CAPITAL LETTER I WITH MACRON*/
+  0x012B, /*U+012B*/ /**/
+  0x012D, /*U+012C*/ /*LATIN CAPITAL LETTER I WITH BREVE*/
+  0x012D, /*U+012D*/ /**/
+  0x012F, /*U+012E*/ /*LATIN CAPITAL LETTER I WITH OGONEK*/
+  0x012F, /*U+012F*/ /**/
+  0x0069, /*U+0130*/ /*LATIN CAPITAL LETTER I WITH DOT ABOVE*/
+  0x0131, /*U+0131*/ /**/
+  0x0133, /*U+0132*/ /*LATIN CAPITAL LIGATURE IJ*/
+  0x0133, /*U+0133*/ /**/
+  0x0135, /*U+0134*/ /*LATIN CAPITAL LETTER J WITH CIRCUMFLEX*/
+  0x0135, /*U+0135*/ /**/
+  0x0137, /*U+0136*/ /*LATIN CAPITAL LETTER K WITH CEDILLA*/
+  0x0137, /*U+0137*/ /**/
+  0x0138, /*U+0138*/ /**/
+  0x013A, /*U+0139*/ /*LATIN CAPITAL LETTER L WITH ACUTE*/
+  0x013A, /*U+013A*/ /**/
+  0x013C, /*U+013B*/ /*LATIN CAPITAL LETTER L WITH CEDILLA*/
+  0x013C, /*U+013C*/ /**/
+  0x013E, /*U+013D*/ /*LATIN CAPITAL LETTER L WITH CARON*/
+  0x013E, /*U+013E*/ /**/
+  0x0140, /*U+013F*/ /*LATIN CAPITAL LETTER L WITH MIDDLE DOT*/
+  0x0140, /*U+0140*/ /**/
+  0x0142, /*U+0141*/ /*LATIN CAPITAL LETTER L WITH STROKE*/
+  0x0142, /*U+0142*/ /**/
+  0x0144, /*U+0143*/ /*LATIN CAPITAL LETTER N WITH ACUTE*/
+  0x0144, /*U+0144*/ /**/
+  0x0146, /*U+0145*/ /*LATIN CAPITAL LETTER N WITH CEDILLA*/
+  0x0146, /*U+0146*/ /**/
+  0x0148, /*U+0147*/ /*LATIN CAPITAL LETTER N WITH CARON*/
+  0x0148, /*U+0148*/ /**/
+  0x0149, /*U+0149*/ /**/
+  0x014B, /*U+014A*/ /*LATIN CAPITAL LETTER ENG*/
+  0x014B, /*U+014B*/ /**/
+  0x014D, /*U+014C*/ /*LATIN CAPITAL LETTER O WITH MACRON*/
+  0x014D, /*U+014D*/ /**/
+  0x014F, /*U+014E*/ /*LATIN CAPITAL LETTER O WITH BREVE*/
+  0x014F, /*U+014F*/ /**/
+  0x0151, /*U+0150*/ /*LATIN CAPITAL LETTER O WITH DOUBLE ACUTE*/
+  0x0151, /*U+0151*/ /**/
+  0x0153, /*U+0152*/ /*LATIN CAPITAL LIGATURE OE*/
+  0x0153, /*U+0153*/ /**/
+  0x0155, /*U+0154*/ /*LATIN CAPITAL LETTER R WITH ACUTE*/
+  0x0155, /*U+0155*/ /**/
+  0x0157, /*U+0156*/ /*LATIN CAPITAL LETTER R WITH CEDILLA*/
+  0x0157, /*U+0157*/ /**/
+  0x0159, /*U+0158*/ /*LATIN CAPITAL LETTER R WITH CARON*/
+  0x0159, /*U+0159*/ /**/
+  0x015B, /*U+015A*/ /*LATIN CAPITAL LETTER S WITH ACUTE*/
+  0x015B, /*U+015B*/ /**/
+  0x015D, /*U+015C*/ /*LATIN CAPITAL LETTER S WITH CIRCUMFLEX*/
+  0x015D, /*U+015D*/ /**/
+  0x015F, /*U+015E*/ /*LATIN CAPITAL LETTER S WITH CEDILLA*/
+  0x015F, /*U+015F*/ /**/
+  0x0161, /*U+0160*/ /*LATIN CAPITAL LETTER S WITH CARON*/
+  0x0161, /*U+0161*/ /**/
+  0x0163, /*U+0162*/ /*LATIN CAPITAL LETTER T WITH CEDILLA*/
+  0x0163, /*U+0163*/ /**/
+  0x0165, /*U+0164*/ /*LATIN CAPITAL LETTER T WITH CARON*/
+  0x0165, /*U+0165*/ /**/
+  0x0167, /*U+0166*/ /*LATIN CAPITAL LETTER T WITH STROKE*/
+  0x0167, /*U+0167*/ /**/
+  0x0169, /*U+0168*/ /*LATIN CAPITAL LETTER U WITH TILDE*/
+  0x0169, /*U+0169*/ /**/
+  0x016B, /*U+016A*/ /*LATIN CAPITAL LETTER U WITH MACRON*/
+  0x016B, /*U+016B*/ /**/
+  0x016D, /*U+016C*/ /*LATIN CAPITAL LETTER U WITH BREVE*/
+  0x016D, /*U+016D*/ /**/
+  0x016F, /*U+016E*/ /*LATIN CAPITAL LETTER U WITH RING ABOVE*/
+  0x016F, /*U+016F*/ /**/
+  0x0171, /*U+0170*/ /*LATIN CAPITAL LETTER U WITH DOUBLE ACUTE*/
+  0x0171, /*U+0171*/ /**/
+  0x0173, /*U+0172*/ /*LATIN CAPITAL LETTER U WITH OGONEK*/
+  0x0173, /*U+0173*/ /**/
+  0x0175, /*U+0174*/ /*LATIN CAPITAL LETTER W WITH CIRCUMFLEX*/
+  0x0175, /*U+0175*/ /**/
+  0x0177, /*U+0176*/ /*LATIN CAPITAL LETTER Y WITH CIRCUMFLEX*/
+  0x0177, /*U+0177*/ /**/
+  0x00FF, /*U+0178*/ /*LATIN CAPITAL LETTER Y WITH DIAERESIS*/
+  0x017A, /*U+0179*/ /*LATIN CAPITAL LETTER Z WITH ACUTE*/
+  0x017A, /*U+017A*/ /**/
+  0x017C, /*U+017B*/ /*LATIN CAPITAL LETTER Z WITH DOT ABOVE*/
+  0x017C, /*U+017C*/ /**/
+  0x017E, /*U+017D*/ /*LATIN CAPITAL LETTER Z WITH CARON*/
+  0x017E, /*U+017E*/ /**/
+  0x017F, /*U+017F*/ /**/
+  0x0180, /*U+0180*/ /**/
+  0x0253, /*U+0181*/ /*LATIN CAPITAL LETTER B WITH HOOK*/
+  0x0183, /*U+0182*/ /*LATIN CAPITAL LETTER B WITH TOPBAR*/
+  0x0183, /*U+0183*/ /**/
+  0x0185, /*U+0184*/ /*LATIN CAPITAL LETTER TONE SIX*/
+  0x0185, /*U+0185*/ /**/
+  0x0254, /*U+0186*/ /*LATIN CAPITAL LETTER OPEN O*/
+  0x0188, /*U+0187*/ /*LATIN CAPITAL LETTER C WITH HOOK*/
+  0x0188, /*U+0188*/ /**/
+  0x0256, /*U+0189*/ /*LATIN CAPITAL LETTER AFRICAN D*/
+  0x0257, /*U+018A*/ /*LATIN CAPITAL LETTER D WITH HOOK*/
+  0x018C, /*U+018B*/ /*LATIN CAPITAL LETTER D WITH TOPBAR*/
+  0x018C, /*U+018C*/ /**/
+  0x018D, /*U+018D*/ /**/
+  0x01DD, /*U+018E*/ /*LATIN CAPITAL LETTER REVERSED E*/
+  0x0259, /*U+018F*/ /*LATIN CAPITAL LETTER SCHWA*/
+  0x025B, /*U+0190*/ /*LATIN CAPITAL LETTER OPEN E*/
+  0x0192, /*U+0191*/ /*LATIN CAPITAL LETTER F WITH HOOK*/
+  0x0192, /*U+0192*/ /**/
+  0x0260, /*U+0193*/ /*LATIN CAPITAL LETTER G WITH HOOK*/
+  0x0263, /*U+0194*/ /*LATIN CAPITAL LETTER GAMMA*/
+  0x0195, /*U+0195*/ /**/
+  0x0269, /*U+0196*/ /*LATIN CAPITAL LETTER IOTA*/
+  0x0268, /*U+0197*/ /*LATIN CAPITAL LETTER I WITH STROKE*/
+  0x0199, /*U+0198*/ /*LATIN CAPITAL LETTER K WITH HOOK*/
+  0x0199, /*U+0199*/ /**/
+  0x019A, /*U+019A*/ /**/
+  0x019B, /*U+019B*/ /**/
+  0x026F, /*U+019C*/ /*LATIN CAPITAL LETTER TURNED M*/
+  0x0272, /*U+019D*/ /*LATIN CAPITAL LETTER N WITH LEFT HOOK*/
+  0x019E, /*U+019E*/ /**/
+  0x0275, /*U+019F*/ /*LATIN CAPITAL LETTER O WITH MIDDLE TILDE*/
+  0x01A1, /*U+01A0*/ /*LATIN CAPITAL LETTER O WITH HORN*/
+  0x01A1, /*U+01A1*/ /**/
+  0x01A3, /*U+01A2*/ /*LATIN CAPITAL LETTER OI*/
+  0x01A3, /*U+01A3*/ /**/
+  0x01A5, /*U+01A4*/ /*LATIN CAPITAL LETTER P WITH HOOK*/
+  0x01A5, /*U+01A5*/ /**/
+  0x0280, /*U+01A6*/ /*LATIN LETTER YR*/
+  0x01A8, /*U+01A7*/ /*LATIN CAPITAL LETTER TONE TWO*/
+  0x01A8, /*U+01A8*/ /**/
+  0x0283, /*U+01A9*/ /*LATIN CAPITAL LETTER ESH*/
+  0x01AA, /*U+01AA*/ /**/
+  0x01AB, /*U+01AB*/ /**/
+  0x01AD, /*U+01AC*/ /*LATIN CAPITAL LETTER T WITH HOOK*/
+  0x01AD, /*U+01AD*/ /**/
+  0x0288, /*U+01AE*/ /*LATIN CAPITAL LETTER T WITH RETROFLEX HOOK*/
+  0x01B0, /*U+01AF*/ /*LATIN CAPITAL LETTER U WITH HORN*/
+  0x01B0, /*U+01B0*/ /**/
+  0x028A, /*U+01B1*/ /*LATIN CAPITAL LETTER UPSILON*/
+  0x028B, /*U+01B2*/ /*LATIN CAPITAL LETTER V WITH HOOK*/
+  0x01B4, /*U+01B3*/ /*LATIN CAPITAL LETTER Y WITH HOOK*/
+  0x01B4, /*U+01B4*/ /**/
+  0x01B6, /*U+01B5*/ /*LATIN CAPITAL LETTER Z WITH STROKE*/
+  0x01B6, /*U+01B6*/ /**/
+  0x0292, /*U+01B7*/ /*LATIN CAPITAL LETTER EZH*/
+  0x01B9, /*U+01B8*/ /*LATIN CAPITAL LETTER EZH REVERSED*/
+  0x01B9, /*U+01B9*/ /**/
+  0x01BA, /*U+01BA*/ /**/
+  0x01BB, /*U+01BB*/ /**/
+  0x01BD, /*U+01BC*/ /*LATIN CAPITAL LETTER TONE FIVE*/
+  0x01BD, /*U+01BD*/ /**/
+  0x01BE, /*U+01BE*/ /**/
+  0x01BF, /*U+01BF*/ /**/
+  0x01C0, /*U+01C0*/ /**/
+  0x01C1, /*U+01C1*/ /**/
+  0x01C2, /*U+01C2*/ /**/
+  0x01C3, /*U+01C3*/ /**/
+  0x01C6, /*U+01C4*/ /*LATIN CAPITAL LETTER DZ WITH CARON*/
+  0x01C6, /*U+01C5*/ /*LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON*/
+  0x01C6, /*U+01C6*/ /**/
+  0x01C9, /*U+01C7*/ /*LATIN CAPITAL LETTER LJ*/
+  0x01C9, /*U+01C8*/ /*LATIN CAPITAL LETTER L WITH SMALL LETTER J*/
+  0x01C9, /*U+01C9*/ /**/
+  0x01CC, /*U+01CA*/ /*LATIN CAPITAL LETTER NJ*/
+  0x01CC, /*U+01CB*/ /*LATIN CAPITAL LETTER N WITH SMALL LETTER J*/
+  0x01CC, /*U+01CC*/ /**/
+  0x01CE, /*U+01CD*/ /*LATIN CAPITAL LETTER A WITH CARON*/
+  0x01CE, /*U+01CE*/ /**/
+  0x01D0, /*U+01CF*/ /*LATIN CAPITAL LETTER I WITH CARON*/
+  0x01D0, /*U+01D0*/ /**/
+  0x01D2, /*U+01D1*/ /*LATIN CAPITAL LETTER O WITH CARON*/
+  0x01D2, /*U+01D2*/ /**/
+  0x01D4, /*U+01D3*/ /*LATIN CAPITAL LETTER U WITH CARON*/
+  0x01D4, /*U+01D4*/ /**/
+  0x01D6, /*U+01D5*/ /*LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON*/
+  0x01D6, /*U+01D6*/ /**/
+  0x01D8, /*U+01D7*/ /*LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE*/
+  0x01D8, /*U+01D8*/ /**/
+  0x01DA, /*U+01D9*/ /*LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON*/
+  0x01DA, /*U+01DA*/ /**/
+  0x01DC, /*U+01DB*/ /*LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE*/
+  0x01DC, /*U+01DC*/ /**/
+  0x01DD, /*U+01DD*/ /**/
+  0x01DF, /*U+01DE*/ /*LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON*/
+  0x01DF, /*U+01DF*/ /**/
+  0x01E1, /*U+01E0*/ /*LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON*/
+  0x01E1, /*U+01E1*/ /**/
+  0x01E3, /*U+01E2*/ /*LATIN CAPITAL LETTER AE WITH MACRON*/
+  0x01E3, /*U+01E3*/ /**/
+  0x01E5, /*U+01E4*/ /*LATIN CAPITAL LETTER G WITH STROKE*/
+  0x01E5, /*U+01E5*/ /**/
+  0x01E7, /*U+01E6*/ /*LATIN CAPITAL LETTER G WITH CARON*/
+  0x01E7, /*U+01E7*/ /**/
+  0x01E9, /*U+01E8*/ /*LATIN CAPITAL LETTER K WITH CARON*/
+  0x01E9, /*U+01E9*/ /**/
+  0x01EB, /*U+01EA*/ /*LATIN CAPITAL LETTER O WITH OGONEK*/
+  0x01EB, /*U+01EB*/ /**/
+  0x01ED, /*U+01EC*/ /*LATIN CAPITAL LETTER O WITH OGONEK AND MACRON*/
+  0x01ED, /*U+01ED*/ /**/
+  0x01EF, /*U+01EE*/ /*LATIN CAPITAL LETTER EZH WITH CARON*/
+  0x01EF, /*U+01EF*/ /**/
+  0x01F0, /*U+01F0*/ /**/
+  0x01F3, /*U+01F1*/ /*LATIN CAPITAL LETTER DZ*/
+  0x01F3, /*U+01F2*/ /*LATIN CAPITAL LETTER D WITH SMALL LETTER Z*/
+  0x01F3, /*U+01F3*/ /**/
+  0x01F5, /*U+01F4*/ /*LATIN CAPITAL LETTER G WITH ACUTE*/
+  0x01F5, /*U+01F5*/ /**/
+  0x0195, /*U+01F6*/ /*LATIN CAPITAL LETTER HWAIR*/
+  0x01BF, /*U+01F7*/ /*LATIN CAPITAL LETTER WYNN*/
+  0x01F9, /*U+01F8*/ /*LATIN CAPITAL LETTER N WITH GRAVE*/
+  0x01F9, /*U+01F9*/ /**/
+  0x01FB, /*U+01FA*/ /*LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE*/
+  0x01FB, /*U+01FB*/ /**/
+  0x01FD, /*U+01FC*/ /*LATIN CAPITAL LETTER AE WITH ACUTE*/
+  0x01FD, /*U+01FD*/ /**/
+  0x01FF, /*U+01FE*/ /*LATIN CAPITAL LETTER O WITH STROKE AND ACUTE*/
+  0x01FF, /*U+01FF*/ /**/
+  0x0201, /*U+0200*/ /*LATIN CAPITAL LETTER A WITH DOUBLE GRAVE*/
+  0x0201, /*U+0201*/ /**/
+  0x0203, /*U+0202*/ /*LATIN CAPITAL LETTER A WITH INVERTED BREVE*/
+  0x0203, /*U+0203*/ /**/
+  0x0205, /*U+0204*/ /*LATIN CAPITAL LETTER E WITH DOUBLE GRAVE*/
+  0x0205, /*U+0205*/ /**/
+  0x0207, /*U+0206*/ /*LATIN CAPITAL LETTER E WITH INVERTED BREVE*/
+  0x0207, /*U+0207*/ /**/
+  0x0209, /*U+0208*/ /*LATIN CAPITAL LETTER I WITH DOUBLE GRAVE*/
+  0x0209, /*U+0209*/ /**/
+  0x020B, /*U+020A*/ /*LATIN CAPITAL LETTER I WITH INVERTED BREVE*/
+  0x020B, /*U+020B*/ /**/
+  0x020D, /*U+020C*/ /*LATIN CAPITAL LETTER O WITH DOUBLE GRAVE*/
+  0x020D, /*U+020D*/ /**/
+  0x020F, /*U+020E*/ /*LATIN CAPITAL LETTER O WITH INVERTED BREVE*/
+  0x020F, /*U+020F*/ /**/
+  0x0211, /*U+0210*/ /*LATIN CAPITAL LETTER R WITH DOUBLE GRAVE*/
+  0x0211, /*U+0211*/ /**/
+  0x0213, /*U+0212*/ /*LATIN CAPITAL LETTER R WITH INVERTED BREVE*/
+  0x0213, /*U+0213*/ /**/
+  0x0215, /*U+0214*/ /*LATIN CAPITAL LETTER U WITH DOUBLE GRAVE*/
+  0x0215, /*U+0215*/ /**/
+  0x0217, /*U+0216*/ /*LATIN CAPITAL LETTER U WITH INVERTED BREVE*/
+  0x0217, /*U+0217*/ /**/
+  0x0219, /*U+0218*/ /*LATIN CAPITAL LETTER S WITH COMMA BELOW*/
+  0x0219, /*U+0219*/ /**/
+  0x021B, /*U+021A*/ /*LATIN CAPITAL LETTER T WITH COMMA BELOW*/
+  0x021B, /*U+021B*/ /**/
+  0x021D, /*U+021C*/ /*LATIN CAPITAL LETTER YOGH*/
+  0x021D, /*U+021D*/ /**/
+  0x021F, /*U+021E*/ /*LATIN CAPITAL LETTER H WITH CARON*/
+  0x021F, /*U+021F*/ /**/
+  0x019E, /*U+0220*/ /*LATIN CAPITAL LETTER N WITH LONG RIGHT LEG*/
+  0x0221, /*U+0221*/ /**/
+  0x0223, /*U+0222*/ /*LATIN CAPITAL LETTER OU*/
+  0x0223, /*U+0223*/ /**/
+  0x0225, /*U+0224*/ /*LATIN CAPITAL LETTER Z WITH HOOK*/
+  0x0225, /*U+0225*/ /**/
+  0x0227, /*U+0226*/ /*LATIN CAPITAL LETTER A WITH DOT ABOVE*/
+  0x0227, /*U+0227*/ /**/
+  0x0229, /*U+0228*/ /*LATIN CAPITAL LETTER E WITH CEDILLA*/
+  0x0229, /*U+0229*/ /**/
+  0x022B, /*U+022A*/ /*LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON*/
+  0x022B, /*U+022B*/ /**/
+  0x022D, /*U+022C*/ /*LATIN CAPITAL LETTER O WITH TILDE AND MACRON*/
+  0x022D, /*U+022D*/ /**/
+  0x022F, /*U+022E*/ /*LATIN CAPITAL LETTER O WITH DOT ABOVE*/
+  0x022F, /*U+022F*/ /**/
+  0x0231, /*U+0230*/ /*LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON*/
+  0x0231, /*U+0231*/ /**/
+  0x0233, /*U+0232*/ /*LATIN CAPITAL LETTER Y WITH MACRON*/
+  0x0233, /*U+0233*/ /**/
+  0x0234, /*U+0234*/ /**/
+  0x0235, /*U+0235*/ /**/
+  0x0236, /*U+0236*/ /**/
+  0x0237, /*U+0237*/ /**/
+  0x0238, /*U+0238*/ /**/
+  0x0239, /*U+0239*/ /**/
+  0x2C65, /*U+023A*/ /*LATIN CAPITAL LETTER A WITH STROKE*/
+  0x023C, /*U+023B*/ /*LATIN CAPITAL LETTER C WITH STROKE*/
+  0x023C, /*U+023C*/ /**/
+  0x019A, /*U+023D*/ /*LATIN CAPITAL LETTER L WITH BAR*/
+  0x2C66, /*U+023E*/ /*LATIN CAPITAL LETTER T WITH DIAGONAL STROKE*/
+  0x023F, /*U+023F*/ /**/
+  0x0240, /*U+0240*/ /**/
+  0x0242, /*U+0241*/ /*LATIN CAPITAL LETTER GLOTTAL STOP*/
+  0x0242, /*U+0242*/ /**/
+  0x0180, /*U+0243*/ /*LATIN CAPITAL LETTER B WITH STROKE*/
+  0x0289, /*U+0244*/ /*LATIN CAPITAL LETTER U BAR*/
+  0x028C, /*U+0245*/ /*LATIN CAPITAL LETTER TURNED V*/
+  0x0247, /*U+0246*/ /*LATIN CAPITAL LETTER E WITH STROKE*/
+  0x0247, /*U+0247*/ /**/
+  0x0249, /*U+0248*/ /*LATIN CAPITAL LETTER J WITH STROKE*/
+  0x0249, /*U+0249*/ /**/
+  0x024B, /*U+024A*/ /*LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL*/
+  0x024B, /*U+024B*/ /**/
+  0x024D, /*U+024C*/ /*LATIN CAPITAL LETTER R WITH STROKE*/
+  0x024D, /*U+024D*/ /**/
+  0x024F, /*U+024E*/ /*LATIN CAPITAL LETTER Y WITH STROKE*/
+  0x024F, /*U+024F*/ /**/
+  0x0250, /*U+0250*/ /**/
+  0x0251, /*U+0251*/ /**/
+  0x0252, /*U+0252*/ /**/
+  0x0253, /*U+0253*/ /**/
+  0x0254, /*U+0254*/ /**/
+  0x0255, /*U+0255*/ /**/
+  0x0256, /*U+0256*/ /**/
+  0x0257, /*U+0257*/ /**/
+  0x0258, /*U+0258*/ /**/
+  0x0259, /*U+0259*/ /**/
+  0x025A, /*U+025A*/ /**/
+  0x025B, /*U+025B*/ /**/
+  0x025C, /*U+025C*/ /**/
+  0x025D, /*U+025D*/ /**/
+  0x025E, /*U+025E*/ /**/
+  0x025F, /*U+025F*/ /**/
+  0x0260, /*U+0260*/ /**/
+  0x0261, /*U+0261*/ /**/
+  0x0262, /*U+0262*/ /**/
+  0x0263, /*U+0263*/ /**/
+  0x0264, /*U+0264*/ /**/
+  0x0265, /*U+0265*/ /**/
+  0x0266, /*U+0266*/ /**/
+  0x0267, /*U+0267*/ /**/
+  0x0268, /*U+0268*/ /**/
+  0x0269, /*U+0269*/ /**/
+  0x026A, /*U+026A*/ /**/
+  0x026B, /*U+026B*/ /**/
+  0x026C, /*U+026C*/ /**/
+  0x026D, /*U+026D*/ /**/
+  0x026E, /*U+026E*/ /**/
+  0x026F, /*U+026F*/ /**/
+  0x0270, /*U+0270*/ /**/
+  0x0271, /*U+0271*/ /**/
+  0x0272, /*U+0272*/ /**/
+  0x0273, /*U+0273*/ /**/
+  0x0274, /*U+0274*/ /**/
+  0x0275, /*U+0275*/ /**/
+  0x0276, /*U+0276*/ /**/
+  0x0277, /*U+0277*/ /**/
+  0x0278, /*U+0278*/ /**/
+  0x0279, /*U+0279*/ /**/
+  0x027A, /*U+027A*/ /**/
+  0x027B, /*U+027B*/ /**/
+  0x027C, /*U+027C*/ /**/
+  0x027D, /*U+027D*/ /**/
+  0x027E, /*U+027E*/ /**/
+  0x027F, /*U+027F*/ /**/
+};
+
+static const u_int16_t lower_table_3[576] = {
+  0x0340, /*U+0340*/ /**/
+  0x0341, /*U+0341*/ /**/
+  0x0342, /*U+0342*/ /**/
+  0x0343, /*U+0343*/ /**/
+  0x0344, /*U+0344*/ /**/
+  0x0345, /*U+0345*/ /**/
+  0x0346, /*U+0346*/ /**/
+  0x0347, /*U+0347*/ /**/
+  0x0348, /*U+0348*/ /**/
+  0x0349, /*U+0349*/ /**/
+  0x034A, /*U+034A*/ /**/
+  0x034B, /*U+034B*/ /**/
+  0x034C, /*U+034C*/ /**/
+  0x034D, /*U+034D*/ /**/
+  0x034E, /*U+034E*/ /**/
+  0x034F, /*U+034F*/ /**/
+  0x0350, /*U+0350*/ /**/
+  0x0351, /*U+0351*/ /**/
+  0x0352, /*U+0352*/ /**/
+  0x0353, /*U+0353*/ /**/
+  0x0354, /*U+0354*/ /**/
+  0x0355, /*U+0355*/ /**/
+  0x0356, /*U+0356*/ /**/
+  0x0357, /*U+0357*/ /**/
+  0x0358, /*U+0358*/ /**/
+  0x0359, /*U+0359*/ /**/
+  0x035A, /*U+035A*/ /**/
+  0x035B, /*U+035B*/ /**/
+  0x035C, /*U+035C*/ /**/
+  0x035D, /*U+035D*/ /**/
+  0x035E, /*U+035E*/ /**/
+  0x035F, /*U+035F*/ /**/
+  0x0360, /*U+0360*/ /**/
+  0x0361, /*U+0361*/ /**/
+  0x0362, /*U+0362*/ /**/
+  0x0363, /*U+0363*/ /**/
+  0x0364, /*U+0364*/ /**/
+  0x0365, /*U+0365*/ /**/
+  0x0366, /*U+0366*/ /**/
+  0x0367, /*U+0367*/ /**/
+  0x0368, /*U+0368*/ /**/
+  0x0369, /*U+0369*/ /**/
+  0x036A, /*U+036A*/ /**/
+  0x036B, /*U+036B*/ /**/
+  0x036C, /*U+036C*/ /**/
+  0x036D, /*U+036D*/ /**/
+  0x036E, /*U+036E*/ /**/
+  0x036F, /*U+036F*/ /**/
+  0x0371, /*U+0370*/ /*GREEK CAPITAL LETTER HETA*/
+  0x0371, /*U+0371*/ /**/
+  0x0373, /*U+0372*/ /*GREEK CAPITAL LETTER ARCHAIC SAMPI*/
+  0x0373, /*U+0373*/ /**/
+  0x0374, /*U+0374*/ /**/
+  0x0375, /*U+0375*/ /**/
+  0x0377, /*U+0376*/ /*GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA*/
+  0x0377, /*U+0377*/ /**/
+  0x0378, /*U+0378*/ /**/
+  0x0379, /*U+0379*/ /**/
+  0x037A, /*U+037A*/ /**/
+  0x037B, /*U+037B*/ /**/
+  0x037C, /*U+037C*/ /**/
+  0x037D, /*U+037D*/ /**/
+  0x037E, /*U+037E*/ /**/
+  0x037F, /*U+037F*/ /**/
+  0x0380, /*U+0380*/ /**/
+  0x0381, /*U+0381*/ /**/
+  0x0382, /*U+0382*/ /**/
+  0x0383, /*U+0383*/ /**/
+  0x0384, /*U+0384*/ /**/
+  0x0385, /*U+0385*/ /**/
+  0x03AC, /*U+0386*/ /*GREEK CAPITAL LETTER ALPHA WITH TONOS*/
+  0x0387, /*U+0387*/ /**/
+  0x03AD, /*U+0388*/ /*GREEK CAPITAL LETTER EPSILON WITH TONOS*/
+  0x03AE, /*U+0389*/ /*GREEK CAPITAL LETTER ETA WITH TONOS*/
+  0x03AF, /*U+038A*/ /*GREEK CAPITAL LETTER IOTA WITH TONOS*/
+  0x038B, /*U+038B*/ /**/
+  0x03CC, /*U+038C*/ /*GREEK CAPITAL LETTER OMICRON WITH TONOS*/
+  0x038D, /*U+038D*/ /**/
+  0x03CD, /*U+038E*/ /*GREEK CAPITAL LETTER UPSILON WITH TONOS*/
+  0x03CE, /*U+038F*/ /*GREEK CAPITAL LETTER OMEGA WITH TONOS*/
+  0x0390, /*U+0390*/ /**/
+  0x03B1, /*U+0391*/ /*GREEK CAPITAL LETTER ALPHA*/
+  0x03B2, /*U+0392*/ /*GREEK CAPITAL LETTER BETA*/
+  0x03B3, /*U+0393*/ /*GREEK CAPITAL LETTER GAMMA*/
+  0x03B4, /*U+0394*/ /*GREEK CAPITAL LETTER DELTA*/
+  0x03B5, /*U+0395*/ /*GREEK CAPITAL LETTER EPSILON*/
+  0x03B6, /*U+0396*/ /*GREEK CAPITAL LETTER ZETA*/
+  0x03B7, /*U+0397*/ /*GREEK CAPITAL LETTER ETA*/
+  0x03B8, /*U+0398*/ /*GREEK CAPITAL LETTER THETA*/
+  0x03B9, /*U+0399*/ /*GREEK CAPITAL LETTER IOTA*/
+  0x03BA, /*U+039A*/ /*GREEK CAPITAL LETTER KAPPA*/
+  0x03BB, /*U+039B*/ /*GREEK CAPITAL LETTER LAMDA*/
+  0x03BC, /*U+039C*/ /*GREEK CAPITAL LETTER MU*/
+  0x03BD, /*U+039D*/ /*GREEK CAPITAL LETTER NU*/
+  0x03BE, /*U+039E*/ /*GREEK CAPITAL LETTER XI*/
+  0x03BF, /*U+039F*/ /*GREEK CAPITAL LETTER OMICRON*/
+  0x03C0, /*U+03A0*/ /*GREEK CAPITAL LETTER PI*/
+  0x03C1, /*U+03A1*/ /*GREEK CAPITAL LETTER RHO*/
+  0x03A2, /*U+03A2*/ /**/
+  0x03C3, /*U+03A3*/ /*GREEK CAPITAL LETTER SIGMA*/
+  0x03C4, /*U+03A4*/ /*GREEK CAPITAL LETTER TAU*/
+  0x03C5, /*U+03A5*/ /*GREEK CAPITAL LETTER UPSILON*/
+  0x03C6, /*U+03A6*/ /*GREEK CAPITAL LETTER PHI*/
+  0x03C7, /*U+03A7*/ /*GREEK CAPITAL LETTER CHI*/
+  0x03C8, /*U+03A8*/ /*GREEK CAPITAL LETTER PSI*/
+  0x03C9, /*U+03A9*/ /*GREEK CAPITAL LETTER OMEGA*/
+  0x03CA, /*U+03AA*/ /*GREEK CAPITAL LETTER IOTA WITH DIALYTIKA*/
+  0x03CB, /*U+03AB*/ /*GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA*/
+  0x03AC, /*U+03AC*/ /**/
+  0x03AD, /*U+03AD*/ /**/
+  0x03AE, /*U+03AE*/ /**/
+  0x03AF, /*U+03AF*/ /**/
+  0x03B0, /*U+03B0*/ /**/
+  0x03B1, /*U+03B1*/ /**/
+  0x03B2, /*U+03B2*/ /**/
+  0x03B3, /*U+03B3*/ /**/
+  0x03B4, /*U+03B4*/ /**/
+  0x03B5, /*U+03B5*/ /**/
+  0x03B6, /*U+03B6*/ /**/
+  0x03B7, /*U+03B7*/ /**/
+  0x03B8, /*U+03B8*/ /**/
+  0x03B9, /*U+03B9*/ /**/
+  0x03BA, /*U+03BA*/ /**/
+  0x03BB, /*U+03BB*/ /**/
+  0x03BC, /*U+03BC*/ /**/
+  0x03BD, /*U+03BD*/ /**/
+  0x03BE, /*U+03BE*/ /**/
+  0x03BF, /*U+03BF*/ /**/
+  0x03C0, /*U+03C0*/ /**/
+  0x03C1, /*U+03C1*/ /**/
+  0x03C2, /*U+03C2*/ /**/
+  0x03C3, /*U+03C3*/ /**/
+  0x03C4, /*U+03C4*/ /**/
+  0x03C5, /*U+03C5*/ /**/
+  0x03C6, /*U+03C6*/ /**/
+  0x03C7, /*U+03C7*/ /**/
+  0x03C8, /*U+03C8*/ /**/
+  0x03C9, /*U+03C9*/ /**/
+  0x03CA, /*U+03CA*/ /**/
+  0x03CB, /*U+03CB*/ /**/
+  0x03CC, /*U+03CC*/ /**/
+  0x03CD, /*U+03CD*/ /**/
+  0x03CE, /*U+03CE*/ /**/
+  0x03D7, /*U+03CF*/ /*GREEK CAPITAL KAI SYMBOL*/
+  0x03D0, /*U+03D0*/ /**/
+  0x03D1, /*U+03D1*/ /**/
+  0x03D2, /*U+03D2*/ /**/
+  0x03D3, /*U+03D3*/ /**/
+  0x03D4, /*U+03D4*/ /**/
+  0x03D5, /*U+03D5*/ /**/
+  0x03D6, /*U+03D6*/ /**/
+  0x03D7, /*U+03D7*/ /**/
+  0x03D9, /*U+03D8*/ /*GREEK LETTER ARCHAIC KOPPA*/
+  0x03D9, /*U+03D9*/ /**/
+  0x03DB, /*U+03DA*/ /*GREEK LETTER STIGMA*/
+  0x03DB, /*U+03DB*/ /**/
+  0x03DD, /*U+03DC*/ /*GREEK LETTER DIGAMMA*/
+  0x03DD, /*U+03DD*/ /**/
+  0x03DF, /*U+03DE*/ /*GREEK LETTER KOPPA*/
+  0x03DF, /*U+03DF*/ /**/
+  0x03E1, /*U+03E0*/ /*GREEK LETTER SAMPI*/
+  0x03E1, /*U+03E1*/ /**/
+  0x03E3, /*U+03E2*/ /*COPTIC CAPITAL LETTER SHEI*/
+  0x03E3, /*U+03E3*/ /**/
+  0x03E5, /*U+03E4*/ /*COPTIC CAPITAL LETTER FEI*/
+  0x03E5, /*U+03E5*/ /**/
+  0x03E7, /*U+03E6*/ /*COPTIC CAPITAL LETTER KHEI*/
+  0x03E7, /*U+03E7*/ /**/
+  0x03E9, /*U+03E8*/ /*COPTIC CAPITAL LETTER HORI*/
+  0x03E9, /*U+03E9*/ /**/
+  0x03EB, /*U+03EA*/ /*COPTIC CAPITAL LETTER GANGIA*/
+  0x03EB, /*U+03EB*/ /**/
+  0x03ED, /*U+03EC*/ /*COPTIC CAPITAL LETTER SHIMA*/
+  0x03ED, /*U+03ED*/ /**/
+  0x03EF, /*U+03EE*/ /*COPTIC CAPITAL LETTER DEI*/
+  0x03EF, /*U+03EF*/ /**/
+  0x03F0, /*U+03F0*/ /**/
+  0x03F1, /*U+03F1*/ /**/
+  0x03F2, /*U+03F2*/ /**/
+  0x03F3, /*U+03F3*/ /**/
+  0x03B8, /*U+03F4*/ /*GREEK CAPITAL THETA SYMBOL*/
+  0x03F5, /*U+03F5*/ /**/
+  0x03F6, /*U+03F6*/ /**/
+  0x03F8, /*U+03F7*/ /*GREEK CAPITAL LETTER SHO*/
+  0x03F8, /*U+03F8*/ /**/
+  0x03F2, /*U+03F9*/ /*GREEK CAPITAL LUNATE SIGMA SYMBOL*/
+  0x03FB, /*U+03FA*/ /*GREEK CAPITAL LETTER SAN*/
+  0x03FB, /*U+03FB*/ /**/
+  0x03FC, /*U+03FC*/ /**/
+  0x037B, /*U+03FD*/ /*GREEK CAPITAL REVERSED LUNATE SIGMA SYMBOL*/
+  0x037C, /*U+03FE*/ /*GREEK CAPITAL DOTTED LUNATE SIGMA SYMBOL*/
+  0x037D, /*U+03FF*/ /*GREEK CAPITAL REVERSED DOTTED LUNATE SIGMA SYMBOL*/
+  0x0450, /*U+0400*/ /*CYRILLIC CAPITAL LETTER IE WITH GRAVE*/
+  0x0451, /*U+0401*/ /*CYRILLIC CAPITAL LETTER IO*/
+  0x0452, /*U+0402*/ /*CYRILLIC CAPITAL LETTER DJE*/
+  0x0453, /*U+0403*/ /*CYRILLIC CAPITAL LETTER GJE*/
+  0x0454, /*U+0404*/ /*CYRILLIC CAPITAL LETTER UKRAINIAN IE*/
+  0x0455, /*U+0405*/ /*CYRILLIC CAPITAL LETTER DZE*/
+  0x0456, /*U+0406*/ /*CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I*/
+  0x0457, /*U+0407*/ /*CYRILLIC CAPITAL LETTER YI*/
+  0x0458, /*U+0408*/ /*CYRILLIC CAPITAL LETTER JE*/
+  0x0459, /*U+0409*/ /*CYRILLIC CAPITAL LETTER LJE*/
+  0x045A, /*U+040A*/ /*CYRILLIC CAPITAL LETTER NJE*/
+  0x045B, /*U+040B*/ /*CYRILLIC CAPITAL LETTER TSHE*/
+  0x045C, /*U+040C*/ /*CYRILLIC CAPITAL LETTER KJE*/
+  0x045D, /*U+040D*/ /*CYRILLIC CAPITAL LETTER I WITH GRAVE*/
+  0x045E, /*U+040E*/ /*CYRILLIC CAPITAL LETTER SHORT U*/
+  0x045F, /*U+040F*/ /*CYRILLIC CAPITAL LETTER DZHE*/
+  0x0430, /*U+0410*/ /*CYRILLIC CAPITAL LETTER A*/
+  0x0431, /*U+0411*/ /*CYRILLIC CAPITAL LETTER BE*/
+  0x0432, /*U+0412*/ /*CYRILLIC CAPITAL LETTER VE*/
+  0x0433, /*U+0413*/ /*CYRILLIC CAPITAL LETTER GHE*/
+  0x0434, /*U+0414*/ /*CYRILLIC CAPITAL LETTER DE*/
+  0x0435, /*U+0415*/ /*CYRILLIC CAPITAL LETTER IE*/
+  0x0436, /*U+0416*/ /*CYRILLIC CAPITAL LETTER ZHE*/
+  0x0437, /*U+0417*/ /*CYRILLIC CAPITAL LETTER ZE*/
+  0x0438, /*U+0418*/ /*CYRILLIC CAPITAL LETTER I*/
+  0x0439, /*U+0419*/ /*CYRILLIC CAPITAL LETTER SHORT I*/
+  0x043A, /*U+041A*/ /*CYRILLIC CAPITAL LETTER KA*/
+  0x043B, /*U+041B*/ /*CYRILLIC CAPITAL LETTER EL*/
+  0x043C, /*U+041C*/ /*CYRILLIC CAPITAL LETTER EM*/
+  0x043D, /*U+041D*/ /*CYRILLIC CAPITAL LETTER EN*/
+  0x043E, /*U+041E*/ /*CYRILLIC CAPITAL LETTER O*/
+  0x043F, /*U+041F*/ /*CYRILLIC CAPITAL LETTER PE*/
+  0x0440, /*U+0420*/ /*CYRILLIC CAPITAL LETTER ER*/
+  0x0441, /*U+0421*/ /*CYRILLIC CAPITAL LETTER ES*/
+  0x0442, /*U+0422*/ /*CYRILLIC CAPITAL LETTER TE*/
+  0x0443, /*U+0423*/ /*CYRILLIC CAPITAL LETTER U*/
+  0x0444, /*U+0424*/ /*CYRILLIC CAPITAL LETTER EF*/
+  0x0445, /*U+0425*/ /*CYRILLIC CAPITAL LETTER HA*/
+  0x0446, /*U+0426*/ /*CYRILLIC CAPITAL LETTER TSE*/
+  0x0447, /*U+0427*/ /*CYRILLIC CAPITAL LETTER CHE*/
+  0x0448, /*U+0428*/ /*CYRILLIC CAPITAL LETTER SHA*/
+  0x0449, /*U+0429*/ /*CYRILLIC CAPITAL LETTER SHCHA*/
+  0x044A, /*U+042A*/ /*CYRILLIC CAPITAL LETTER HARD SIGN*/
+  0x044B, /*U+042B*/ /*CYRILLIC CAPITAL LETTER YERU*/
+  0x044C, /*U+042C*/ /*CYRILLIC CAPITAL LETTER SOFT SIGN*/
+  0x044D, /*U+042D*/ /*CYRILLIC CAPITAL LETTER E*/
+  0x044E, /*U+042E*/ /*CYRILLIC CAPITAL LETTER YU*/
+  0x044F, /*U+042F*/ /*CYRILLIC CAPITAL LETTER YA*/
+  0x0430, /*U+0430*/ /**/
+  0x0431, /*U+0431*/ /**/
+  0x0432, /*U+0432*/ /**/
+  0x0433, /*U+0433*/ /**/
+  0x0434, /*U+0434*/ /**/
+  0x0435, /*U+0435*/ /**/
+  0x0436, /*U+0436*/ /**/
+  0x0437, /*U+0437*/ /**/
+  0x0438, /*U+0438*/ /**/
+  0x0439, /*U+0439*/ /**/
+  0x043A, /*U+043A*/ /**/
+  0x043B, /*U+043B*/ /**/
+  0x043C, /*U+043C*/ /**/
+  0x043D, /*U+043D*/ /**/
+  0x043E, /*U+043E*/ /**/
+  0x043F, /*U+043F*/ /**/
+  0x0440, /*U+0440*/ /**/
+  0x0441, /*U+0441*/ /**/
+  0x0442, /*U+0442*/ /**/
+  0x0443, /*U+0443*/ /**/
+  0x0444, /*U+0444*/ /**/
+  0x0445, /*U+0445*/ /**/
+  0x0446, /*U+0446*/ /**/
+  0x0447, /*U+0447*/ /**/
+  0x0448, /*U+0448*/ /**/
+  0x0449, /*U+0449*/ /**/
+  0x044A, /*U+044A*/ /**/
+  0x044B, /*U+044B*/ /**/
+  0x044C, /*U+044C*/ /**/
+  0x044D, /*U+044D*/ /**/
+  0x044E, /*U+044E*/ /**/
+  0x044F, /*U+044F*/ /**/
+  0x0450, /*U+0450*/ /**/
+  0x0451, /*U+0451*/ /**/
+  0x0452, /*U+0452*/ /**/
+  0x0453, /*U+0453*/ /**/
+  0x0454, /*U+0454*/ /**/
+  0x0455, /*U+0455*/ /**/
+  0x0456, /*U+0456*/ /**/
+  0x0457, /*U+0457*/ /**/
+  0x0458, /*U+0458*/ /**/
+  0x0459, /*U+0459*/ /**/
+  0x045A, /*U+045A*/ /**/
+  0x045B, /*U+045B*/ /**/
+  0x045C, /*U+045C*/ /**/
+  0x045D, /*U+045D*/ /**/
+  0x045E, /*U+045E*/ /**/
+  0x045F, /*U+045F*/ /**/
+  0x0461, /*U+0460*/ /*CYRILLIC CAPITAL LETTER OMEGA*/
+  0x0461, /*U+0461*/ /**/
+  0x0463, /*U+0462*/ /*CYRILLIC CAPITAL LETTER YAT*/
+  0x0463, /*U+0463*/ /**/
+  0x0465, /*U+0464*/ /*CYRILLIC CAPITAL LETTER IOTIFIED E*/
+  0x0465, /*U+0465*/ /**/
+  0x0467, /*U+0466*/ /*CYRILLIC CAPITAL LETTER LITTLE YUS*/
+  0x0467, /*U+0467*/ /**/
+  0x0469, /*U+0468*/ /*CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS*/
+  0x0469, /*U+0469*/ /**/
+  0x046B, /*U+046A*/ /*CYRILLIC CAPITAL LETTER BIG YUS*/
+  0x046B, /*U+046B*/ /**/
+  0x046D, /*U+046C*/ /*CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS*/
+  0x046D, /*U+046D*/ /**/
+  0x046F, /*U+046E*/ /*CYRILLIC CAPITAL LETTER KSI*/
+  0x046F, /*U+046F*/ /**/
+  0x0471, /*U+0470*/ /*CYRILLIC CAPITAL LETTER PSI*/
+  0x0471, /*U+0471*/ /**/
+  0x0473, /*U+0472*/ /*CYRILLIC CAPITAL LETTER FITA*/
+  0x0473, /*U+0473*/ /**/
+  0x0475, /*U+0474*/ /*CYRILLIC CAPITAL LETTER IZHITSA*/
+  0x0475, /*U+0475*/ /**/
+  0x0477, /*U+0476*/ /*CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT*/
+  0x0477, /*U+0477*/ /**/
+  0x0479, /*U+0478*/ /*CYRILLIC CAPITAL LETTER UK*/
+  0x0479, /*U+0479*/ /**/
+  0x047B, /*U+047A*/ /*CYRILLIC CAPITAL LETTER ROUND OMEGA*/
+  0x047B, /*U+047B*/ /**/
+  0x047D, /*U+047C*/ /*CYRILLIC CAPITAL LETTER OMEGA WITH TITLO*/
+  0x047D, /*U+047D*/ /**/
+  0x047F, /*U+047E*/ /*CYRILLIC CAPITAL LETTER OT*/
+  0x047F, /*U+047F*/ /**/
+  0x0481, /*U+0480*/ /*CYRILLIC CAPITAL LETTER KOPPA*/
+  0x0481, /*U+0481*/ /**/
+  0x0482, /*U+0482*/ /**/
+  0x0483, /*U+0483*/ /**/
+  0x0484, /*U+0484*/ /**/
+  0x0485, /*U+0485*/ /**/
+  0x0486, /*U+0486*/ /**/
+  0x0487, /*U+0487*/ /**/
+  0x0488, /*U+0488*/ /**/
+  0x0489, /*U+0489*/ /**/
+  0x048B, /*U+048A*/ /*CYRILLIC CAPITAL LETTER SHORT I WITH TAIL*/
+  0x048B, /*U+048B*/ /**/
+  0x048D, /*U+048C*/ /*CYRILLIC CAPITAL LETTER SEMISOFT SIGN*/
+  0x048D, /*U+048D*/ /**/
+  0x048F, /*U+048E*/ /*CYRILLIC CAPITAL LETTER ER WITH TICK*/
+  0x048F, /*U+048F*/ /**/
+  0x0491, /*U+0490*/ /*CYRILLIC CAPITAL LETTER GHE WITH UPTURN*/
+  0x0491, /*U+0491*/ /**/
+  0x0493, /*U+0492*/ /*CYRILLIC CAPITAL LETTER GHE WITH STROKE*/
+  0x0493, /*U+0493*/ /**/
+  0x0495, /*U+0494*/ /*CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK*/
+  0x0495, /*U+0495*/ /**/
+  0x0497, /*U+0496*/ /*CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER*/
+  0x0497, /*U+0497*/ /**/
+  0x0499, /*U+0498*/ /*CYRILLIC CAPITAL LETTER ZE WITH DESCENDER*/
+  0x0499, /*U+0499*/ /**/
+  0x049B, /*U+049A*/ /*CYRILLIC CAPITAL LETTER KA WITH DESCENDER*/
+  0x049B, /*U+049B*/ /**/
+  0x049D, /*U+049C*/ /*CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE*/
+  0x049D, /*U+049D*/ /**/
+  0x049F, /*U+049E*/ /*CYRILLIC CAPITAL LETTER KA WITH STROKE*/
+  0x049F, /*U+049F*/ /**/
+  0x04A1, /*U+04A0*/ /*CYRILLIC CAPITAL LETTER BASHKIR KA*/
+  0x04A1, /*U+04A1*/ /**/
+  0x04A3, /*U+04A2*/ /*CYRILLIC CAPITAL LETTER EN WITH DESCENDER*/
+  0x04A3, /*U+04A3*/ /**/
+  0x04A5, /*U+04A4*/ /*CYRILLIC CAPITAL LIGATURE EN GHE*/
+  0x04A5, /*U+04A5*/ /**/
+  0x04A7, /*U+04A6*/ /*CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK*/
+  0x04A7, /*U+04A7*/ /**/
+  0x04A9, /*U+04A8*/ /*CYRILLIC CAPITAL LETTER ABKHASIAN HA*/
+  0x04A9, /*U+04A9*/ /**/
+  0x04AB, /*U+04AA*/ /*CYRILLIC CAPITAL LETTER ES WITH DESCENDER*/
+  0x04AB, /*U+04AB*/ /**/
+  0x04AD, /*U+04AC*/ /*CYRILLIC CAPITAL LETTER TE WITH DESCENDER*/
+  0x04AD, /*U+04AD*/ /**/
+  0x04AF, /*U+04AE*/ /*CYRILLIC CAPITAL LETTER STRAIGHT U*/
+  0x04AF, /*U+04AF*/ /**/
+  0x04B1, /*U+04B0*/ /*CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE*/
+  0x04B1, /*U+04B1*/ /**/
+  0x04B3, /*U+04B2*/ /*CYRILLIC CAPITAL LETTER HA WITH DESCENDER*/
+  0x04B3, /*U+04B3*/ /**/
+  0x04B5, /*U+04B4*/ /*CYRILLIC CAPITAL LIGATURE TE TSE*/
+  0x04B5, /*U+04B5*/ /**/
+  0x04B7, /*U+04B6*/ /*CYRILLIC CAPITAL LETTER CHE WITH DESCENDER*/
+  0x04B7, /*U+04B7*/ /**/
+  0x04B9, /*U+04B8*/ /*CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE*/
+  0x04B9, /*U+04B9*/ /**/
+  0x04BB, /*U+04BA*/ /*CYRILLIC CAPITAL LETTER SHHA*/
+  0x04BB, /*U+04BB*/ /**/
+  0x04BD, /*U+04BC*/ /*CYRILLIC CAPITAL LETTER ABKHASIAN CHE*/
+  0x04BD, /*U+04BD*/ /**/
+  0x04BF, /*U+04BE*/ /*CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER*/
+  0x04BF, /*U+04BF*/ /**/
+  0x04CF, /*U+04C0*/ /*CYRILLIC LETTER PALOCHKA*/
+  0x04C2, /*U+04C1*/ /*CYRILLIC CAPITAL LETTER ZHE WITH BREVE*/
+  0x04C2, /*U+04C2*/ /**/
+  0x04C4, /*U+04C3*/ /*CYRILLIC CAPITAL LETTER KA WITH HOOK*/
+  0x04C4, /*U+04C4*/ /**/
+  0x04C6, /*U+04C5*/ /*CYRILLIC CAPITAL LETTER EL WITH TAIL*/
+  0x04C6, /*U+04C6*/ /**/
+  0x04C8, /*U+04C7*/ /*CYRILLIC CAPITAL LETTER EN WITH HOOK*/
+  0x04C8, /*U+04C8*/ /**/
+  0x04CA, /*U+04C9*/ /*CYRILLIC CAPITAL LETTER EN WITH TAIL*/
+  0x04CA, /*U+04CA*/ /**/
+  0x04CC, /*U+04CB*/ /*CYRILLIC CAPITAL LETTER KHAKASSIAN CHE*/
+  0x04CC, /*U+04CC*/ /**/
+  0x04CE, /*U+04CD*/ /*CYRILLIC CAPITAL LETTER EM WITH TAIL*/
+  0x04CE, /*U+04CE*/ /**/
+  0x04CF, /*U+04CF*/ /**/
+  0x04D1, /*U+04D0*/ /*CYRILLIC CAPITAL LETTER A WITH BREVE*/
+  0x04D1, /*U+04D1*/ /**/
+  0x04D3, /*U+04D2*/ /*CYRILLIC CAPITAL LETTER A WITH DIAERESIS*/
+  0x04D3, /*U+04D3*/ /**/
+  0x04D5, /*U+04D4*/ /*CYRILLIC CAPITAL LIGATURE A IE*/
+  0x04D5, /*U+04D5*/ /**/
+  0x04D7, /*U+04D6*/ /*CYRILLIC CAPITAL LETTER IE WITH BREVE*/
+  0x04D7, /*U+04D7*/ /**/
+  0x04D9, /*U+04D8*/ /*CYRILLIC CAPITAL LETTER SCHWA*/
+  0x04D9, /*U+04D9*/ /**/
+  0x04DB, /*U+04DA*/ /*CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS*/
+  0x04DB, /*U+04DB*/ /**/
+  0x04DD, /*U+04DC*/ /*CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS*/
+  0x04DD, /*U+04DD*/ /**/
+  0x04DF, /*U+04DE*/ /*CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS*/
+  0x04DF, /*U+04DF*/ /**/
+  0x04E1, /*U+04E0*/ /*CYRILLIC CAPITAL LETTER ABKHASIAN DZE*/
+  0x04E1, /*U+04E1*/ /**/
+  0x04E3, /*U+04E2*/ /*CYRILLIC CAPITAL LETTER I WITH MACRON*/
+  0x04E3, /*U+04E3*/ /**/
+  0x04E5, /*U+04E4*/ /*CYRILLIC CAPITAL LETTER I WITH DIAERESIS*/
+  0x04E5, /*U+04E5*/ /**/
+  0x04E7, /*U+04E6*/ /*CYRILLIC CAPITAL LETTER O WITH DIAERESIS*/
+  0x04E7, /*U+04E7*/ /**/
+  0x04E9, /*U+04E8*/ /*CYRILLIC CAPITAL LETTER BARRED O*/
+  0x04E9, /*U+04E9*/ /**/
+  0x04EB, /*U+04EA*/ /*CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS*/
+  0x04EB, /*U+04EB*/ /**/
+  0x04ED, /*U+04EC*/ /*CYRILLIC CAPITAL LETTER E WITH DIAERESIS*/
+  0x04ED, /*U+04ED*/ /**/
+  0x04EF, /*U+04EE*/ /*CYRILLIC CAPITAL LETTER U WITH MACRON*/
+  0x04EF, /*U+04EF*/ /**/
+  0x04F1, /*U+04F0*/ /*CYRILLIC CAPITAL LETTER U WITH DIAERESIS*/
+  0x04F1, /*U+04F1*/ /**/
+  0x04F3, /*U+04F2*/ /*CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE*/
+  0x04F3, /*U+04F3*/ /**/
+  0x04F5, /*U+04F4*/ /*CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS*/
+  0x04F5, /*U+04F5*/ /**/
+  0x04F7, /*U+04F6*/ /*CYRILLIC CAPITAL LETTER GHE WITH DESCENDER*/
+  0x04F7, /*U+04F7*/ /**/
+  0x04F9, /*U+04F8*/ /*CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS*/
+  0x04F9, /*U+04F9*/ /**/
+  0x04FB, /*U+04FA*/ /*CYRILLIC CAPITAL LETTER GHE WITH STROKE AND HOOK*/
+  0x04FB, /*U+04FB*/ /**/
+  0x04FD, /*U+04FC*/ /*CYRILLIC CAPITAL LETTER HA WITH HOOK*/
+  0x04FD, /*U+04FD*/ /**/
+  0x04FF, /*U+04FE*/ /*CYRILLIC CAPITAL LETTER HA WITH STROKE*/
+  0x04FF, /*U+04FF*/ /**/
+  0x0501, /*U+0500*/ /*CYRILLIC CAPITAL LETTER KOMI DE*/
+  0x0501, /*U+0501*/ /**/
+  0x0503, /*U+0502*/ /*CYRILLIC CAPITAL LETTER KOMI DJE*/
+  0x0503, /*U+0503*/ /**/
+  0x0505, /*U+0504*/ /*CYRILLIC CAPITAL LETTER KOMI ZJE*/
+  0x0505, /*U+0505*/ /**/
+  0x0507, /*U+0506*/ /*CYRILLIC CAPITAL LETTER KOMI DZJE*/
+  0x0507, /*U+0507*/ /**/
+  0x0509, /*U+0508*/ /*CYRILLIC CAPITAL LETTER KOMI LJE*/
+  0x0509, /*U+0509*/ /**/
+  0x050B, /*U+050A*/ /*CYRILLIC CAPITAL LETTER KOMI NJE*/
+  0x050B, /*U+050B*/ /**/
+  0x050D, /*U+050C*/ /*CYRILLIC CAPITAL LETTER KOMI SJE*/
+  0x050D, /*U+050D*/ /**/
+  0x050F, /*U+050E*/ /*CYRILLIC CAPITAL LETTER KOMI TJE*/
+  0x050F, /*U+050F*/ /**/
+  0x0511, /*U+0510*/ /*CYRILLIC CAPITAL LETTER REVERSED ZE*/
+  0x0511, /*U+0511*/ /**/
+  0x0513, /*U+0512*/ /*CYRILLIC CAPITAL LETTER EL WITH HOOK*/
+  0x0513, /*U+0513*/ /**/
+  0x0515, /*U+0514*/ /*CYRILLIC CAPITAL LETTER LHA*/
+  0x0515, /*U+0515*/ /**/
+  0x0517, /*U+0516*/ /*CYRILLIC CAPITAL LETTER RHA*/
+  0x0517, /*U+0517*/ /**/
+  0x0519, /*U+0518*/ /*CYRILLIC CAPITAL LETTER YAE*/
+  0x0519, /*U+0519*/ /**/
+  0x051B, /*U+051A*/ /*CYRILLIC CAPITAL LETTER QA*/
+  0x051B, /*U+051B*/ /**/
+  0x051D, /*U+051C*/ /*CYRILLIC CAPITAL LETTER WE*/
+  0x051D, /*U+051D*/ /**/
+  0x051F, /*U+051E*/ /*CYRILLIC CAPITAL LETTER ALEUT KA*/
+  0x051F, /*U+051F*/ /**/
+  0x0521, /*U+0520*/ /*CYRILLIC CAPITAL LETTER EL WITH MIDDLE HOOK*/
+  0x0521, /*U+0521*/ /**/
+  0x0523, /*U+0522*/ /*CYRILLIC CAPITAL LETTER EN WITH MIDDLE HOOK*/
+  0x0523, /*U+0523*/ /**/
+  0x0525, /*U+0524*/ /*CYRILLIC CAPITAL LETTER PE WITH DESCENDER*/
+  0x0525, /*U+0525*/ /**/
+  0x0527, /*U+0526*/ /*CYRILLIC CAPITAL LETTER SHHA WITH DESCENDER*/
+  0x0527, /*U+0527*/ /**/
+  0x0528, /*U+0528*/ /**/
+  0x0529, /*U+0529*/ /**/
+  0x052A, /*U+052A*/ /**/
+  0x052B, /*U+052B*/ /**/
+  0x052C, /*U+052C*/ /**/
+  0x052D, /*U+052D*/ /**/
+  0x052E, /*U+052E*/ /**/
+  0x052F, /*U+052F*/ /**/
+  0x0530, /*U+0530*/ /**/
+  0x0561, /*U+0531*/ /*ARMENIAN CAPITAL LETTER AYB*/
+  0x0562, /*U+0532*/ /*ARMENIAN CAPITAL LETTER BEN*/
+  0x0563, /*U+0533*/ /*ARMENIAN CAPITAL LETTER GIM*/
+  0x0564, /*U+0534*/ /*ARMENIAN CAPITAL LETTER DA*/
+  0x0565, /*U+0535*/ /*ARMENIAN CAPITAL LETTER ECH*/
+  0x0566, /*U+0536*/ /*ARMENIAN CAPITAL LETTER ZA*/
+  0x0567, /*U+0537*/ /*ARMENIAN CAPITAL LETTER EH*/
+  0x0568, /*U+0538*/ /*ARMENIAN CAPITAL LETTER ET*/
+  0x0569, /*U+0539*/ /*ARMENIAN CAPITAL LETTER TO*/
+  0x056A, /*U+053A*/ /*ARMENIAN CAPITAL LETTER ZHE*/
+  0x056B, /*U+053B*/ /*ARMENIAN CAPITAL LETTER INI*/
+  0x056C, /*U+053C*/ /*ARMENIAN CAPITAL LETTER LIWN*/
+  0x056D, /*U+053D*/ /*ARMENIAN CAPITAL LETTER XEH*/
+  0x056E, /*U+053E*/ /*ARMENIAN CAPITAL LETTER CA*/
+  0x056F, /*U+053F*/ /*ARMENIAN CAPITAL LETTER KEN*/
+  0x0570, /*U+0540*/ /*ARMENIAN CAPITAL LETTER HO*/
+  0x0571, /*U+0541*/ /*ARMENIAN CAPITAL LETTER JA*/
+  0x0572, /*U+0542*/ /*ARMENIAN CAPITAL LETTER GHAD*/
+  0x0573, /*U+0543*/ /*ARMENIAN CAPITAL LETTER CHEH*/
+  0x0574, /*U+0544*/ /*ARMENIAN CAPITAL LETTER MEN*/
+  0x0575, /*U+0545*/ /*ARMENIAN CAPITAL LETTER YI*/
+  0x0576, /*U+0546*/ /*ARMENIAN CAPITAL LETTER NOW*/
+  0x0577, /*U+0547*/ /*ARMENIAN CAPITAL LETTER SHA*/
+  0x0578, /*U+0548*/ /*ARMENIAN CAPITAL LETTER VO*/
+  0x0579, /*U+0549*/ /*ARMENIAN CAPITAL LETTER CHA*/
+  0x057A, /*U+054A*/ /*ARMENIAN CAPITAL LETTER PEH*/
+  0x057B, /*U+054B*/ /*ARMENIAN CAPITAL LETTER JHEH*/
+  0x057C, /*U+054C*/ /*ARMENIAN CAPITAL LETTER RA*/
+  0x057D, /*U+054D*/ /*ARMENIAN CAPITAL LETTER SEH*/
+  0x057E, /*U+054E*/ /*ARMENIAN CAPITAL LETTER VEW*/
+  0x057F, /*U+054F*/ /*ARMENIAN CAPITAL LETTER TIWN*/
+  0x0580, /*U+0550*/ /*ARMENIAN CAPITAL LETTER REH*/
+  0x0581, /*U+0551*/ /*ARMENIAN CAPITAL LETTER CO*/
+  0x0582, /*U+0552*/ /*ARMENIAN CAPITAL LETTER YIWN*/
+  0x0583, /*U+0553*/ /*ARMENIAN CAPITAL LETTER PIWR*/
+  0x0584, /*U+0554*/ /*ARMENIAN CAPITAL LETTER KEH*/
+  0x0585, /*U+0555*/ /*ARMENIAN CAPITAL LETTER OH*/
+  0x0586, /*U+0556*/ /*ARMENIAN CAPITAL LETTER FEH*/
+  0x0557, /*U+0557*/ /**/
+  0x0558, /*U+0558*/ /**/
+  0x0559, /*U+0559*/ /**/
+  0x055A, /*U+055A*/ /**/
+  0x055B, /*U+055B*/ /**/
+  0x055C, /*U+055C*/ /**/
+  0x055D, /*U+055D*/ /**/
+  0x055E, /*U+055E*/ /**/
+  0x055F, /*U+055F*/ /**/
+  0x0560, /*U+0560*/ /**/
+  0x0561, /*U+0561*/ /**/
+  0x0562, /*U+0562*/ /**/
+  0x0563, /*U+0563*/ /**/
+  0x0564, /*U+0564*/ /**/
+  0x0565, /*U+0565*/ /**/
+  0x0566, /*U+0566*/ /**/
+  0x0567, /*U+0567*/ /**/
+  0x0568, /*U+0568*/ /**/
+  0x0569, /*U+0569*/ /**/
+  0x056A, /*U+056A*/ /**/
+  0x056B, /*U+056B*/ /**/
+  0x056C, /*U+056C*/ /**/
+  0x056D, /*U+056D*/ /**/
+  0x056E, /*U+056E*/ /**/
+  0x056F, /*U+056F*/ /**/
+  0x0570, /*U+0570*/ /**/
+  0x0571, /*U+0571*/ /**/
+  0x0572, /*U+0572*/ /**/
+  0x0573, /*U+0573*/ /**/
+  0x0574, /*U+0574*/ /**/
+  0x0575, /*U+0575*/ /**/
+  0x0576, /*U+0576*/ /**/
+  0x0577, /*U+0577*/ /**/
+  0x0578, /*U+0578*/ /**/
+  0x0579, /*U+0579*/ /**/
+  0x057A, /*U+057A*/ /**/
+  0x057B, /*U+057B*/ /**/
+  0x057C, /*U+057C*/ /**/
+  0x057D, /*U+057D*/ /**/
+  0x057E, /*U+057E*/ /**/
+  0x057F, /*U+057F*/ /**/
+};
+
+static const u_int16_t lower_table_4[128] = {
+  0x1080, /*U+1080*/ /**/
+  0x1081, /*U+1081*/ /**/
+  0x1082, /*U+1082*/ /**/
+  0x1083, /*U+1083*/ /**/
+  0x1084, /*U+1084*/ /**/
+  0x1085, /*U+1085*/ /**/
+  0x1086, /*U+1086*/ /**/
+  0x1087, /*U+1087*/ /**/
+  0x1088, /*U+1088*/ /**/
+  0x1089, /*U+1089*/ /**/
+  0x108A, /*U+108A*/ /**/
+  0x108B, /*U+108B*/ /**/
+  0x108C, /*U+108C*/ /**/
+  0x108D, /*U+108D*/ /**/
+  0x108E, /*U+108E*/ /**/
+  0x108F, /*U+108F*/ /**/
+  0x1090, /*U+1090*/ /**/
+  0x1091, /*U+1091*/ /**/
+  0x1092, /*U+1092*/ /**/
+  0x1093, /*U+1093*/ /**/
+  0x1094, /*U+1094*/ /**/
+  0x1095, /*U+1095*/ /**/
+  0x1096, /*U+1096*/ /**/
+  0x1097, /*U+1097*/ /**/
+  0x1098, /*U+1098*/ /**/
+  0x1099, /*U+1099*/ /**/
+  0x109A, /*U+109A*/ /**/
+  0x109B, /*U+109B*/ /**/
+  0x109C, /*U+109C*/ /**/
+  0x109D, /*U+109D*/ /**/
+  0x109E, /*U+109E*/ /**/
+  0x109F, /*U+109F*/ /**/
+  0x2D00, /*U+10A0*/ /*GEORGIAN CAPITAL LETTER AN*/
+  0x2D01, /*U+10A1*/ /*GEORGIAN CAPITAL LETTER BAN*/
+  0x2D02, /*U+10A2*/ /*GEORGIAN CAPITAL LETTER GAN*/
+  0x2D03, /*U+10A3*/ /*GEORGIAN CAPITAL LETTER DON*/
+  0x2D04, /*U+10A4*/ /*GEORGIAN CAPITAL LETTER EN*/
+  0x2D05, /*U+10A5*/ /*GEORGIAN CAPITAL LETTER VIN*/
+  0x2D06, /*U+10A6*/ /*GEORGIAN CAPITAL LETTER ZEN*/
+  0x2D07, /*U+10A7*/ /*GEORGIAN CAPITAL LETTER TAN*/
+  0x2D08, /*U+10A8*/ /*GEORGIAN CAPITAL LETTER IN*/
+  0x2D09, /*U+10A9*/ /*GEORGIAN CAPITAL LETTER KAN*/
+  0x2D0A, /*U+10AA*/ /*GEORGIAN CAPITAL LETTER LAS*/
+  0x2D0B, /*U+10AB*/ /*GEORGIAN CAPITAL LETTER MAN*/
+  0x2D0C, /*U+10AC*/ /*GEORGIAN CAPITAL LETTER NAR*/
+  0x2D0D, /*U+10AD*/ /*GEORGIAN CAPITAL LETTER ON*/
+  0x2D0E, /*U+10AE*/ /*GEORGIAN CAPITAL LETTER PAR*/
+  0x2D0F, /*U+10AF*/ /*GEORGIAN CAPITAL LETTER ZHAR*/
+  0x2D10, /*U+10B0*/ /*GEORGIAN CAPITAL LETTER RAE*/
+  0x2D11, /*U+10B1*/ /*GEORGIAN CAPITAL LETTER SAN*/
+  0x2D12, /*U+10B2*/ /*GEORGIAN CAPITAL LETTER TAR*/
+  0x2D13, /*U+10B3*/ /*GEORGIAN CAPITAL LETTER UN*/
+  0x2D14, /*U+10B4*/ /*GEORGIAN CAPITAL LETTER PHAR*/
+  0x2D15, /*U+10B5*/ /*GEORGIAN CAPITAL LETTER KHAR*/
+  0x2D16, /*U+10B6*/ /*GEORGIAN CAPITAL LETTER GHAN*/
+  0x2D17, /*U+10B7*/ /*GEORGIAN CAPITAL LETTER QAR*/
+  0x2D18, /*U+10B8*/ /*GEORGIAN CAPITAL LETTER SHIN*/
+  0x2D19, /*U+10B9*/ /*GEORGIAN CAPITAL LETTER CHIN*/
+  0x2D1A, /*U+10BA*/ /*GEORGIAN CAPITAL LETTER CAN*/
+  0x2D1B, /*U+10BB*/ /*GEORGIAN CAPITAL LETTER JIL*/
+  0x2D1C, /*U+10BC*/ /*GEORGIAN CAPITAL LETTER CIL*/
+  0x2D1D, /*U+10BD*/ /*GEORGIAN CAPITAL LETTER CHAR*/
+  0x2D1E, /*U+10BE*/ /*GEORGIAN CAPITAL LETTER XAN*/
+  0x2D1F, /*U+10BF*/ /*GEORGIAN CAPITAL LETTER JHAN*/
+  0x2D20, /*U+10C0*/ /*GEORGIAN CAPITAL LETTER HAE*/
+  0x2D21, /*U+10C1*/ /*GEORGIAN CAPITAL LETTER HE*/
+  0x2D22, /*U+10C2*/ /*GEORGIAN CAPITAL LETTER HIE*/
+  0x2D23, /*U+10C3*/ /*GEORGIAN CAPITAL LETTER WE*/
+  0x2D24, /*U+10C4*/ /*GEORGIAN CAPITAL LETTER HAR*/
+  0x2D25, /*U+10C5*/ /*GEORGIAN CAPITAL LETTER HOE*/
+  0x10C6, /*U+10C6*/ /**/
+  0x10C7, /*U+10C7*/ /**/
+  0x10C8, /*U+10C8*/ /**/
+  0x10C9, /*U+10C9*/ /**/
+  0x10CA, /*U+10CA*/ /**/
+  0x10CB, /*U+10CB*/ /**/
+  0x10CC, /*U+10CC*/ /**/
+  0x10CD, /*U+10CD*/ /**/
+  0x10CE, /*U+10CE*/ /**/
+  0x10CF, /*U+10CF*/ /**/
+  0x10D0, /*U+10D0*/ /**/
+  0x10D1, /*U+10D1*/ /**/
+  0x10D2, /*U+10D2*/ /**/
+  0x10D3, /*U+10D3*/ /**/
+  0x10D4, /*U+10D4*/ /**/
+  0x10D5, /*U+10D5*/ /**/
+  0x10D6, /*U+10D6*/ /**/
+  0x10D7, /*U+10D7*/ /**/
+  0x10D8, /*U+10D8*/ /**/
+  0x10D9, /*U+10D9*/ /**/
+  0x10DA, /*U+10DA*/ /**/
+  0x10DB, /*U+10DB*/ /**/
+  0x10DC, /*U+10DC*/ /**/
+  0x10DD, /*U+10DD*/ /**/
+  0x10DE, /*U+10DE*/ /**/
+  0x10DF, /*U+10DF*/ /**/
+  0x10E0, /*U+10E0*/ /**/
+  0x10E1, /*U+10E1*/ /**/
+  0x10E2, /*U+10E2*/ /**/
+  0x10E3, /*U+10E3*/ /**/
+  0x10E4, /*U+10E4*/ /**/
+  0x10E5, /*U+10E5*/ /**/
+  0x10E6, /*U+10E6*/ /**/
+  0x10E7, /*U+10E7*/ /**/
+  0x10E8, /*U+10E8*/ /**/
+  0x10E9, /*U+10E9*/ /**/
+  0x10EA, /*U+10EA*/ /**/
+  0x10EB, /*U+10EB*/ /**/
+  0x10EC, /*U+10EC*/ /**/
+  0x10ED, /*U+10ED*/ /**/
+  0x10EE, /*U+10EE*/ /**/
+  0x10EF, /*U+10EF*/ /**/
+  0x10F0, /*U+10F0*/ /**/
+  0x10F1, /*U+10F1*/ /**/
+  0x10F2, /*U+10F2*/ /**/
+  0x10F3, /*U+10F3*/ /**/
+  0x10F4, /*U+10F4*/ /**/
+  0x10F5, /*U+10F5*/ /**/
+  0x10F6, /*U+10F6*/ /**/
+  0x10F7, /*U+10F7*/ /**/
+  0x10F8, /*U+10F8*/ /**/
+  0x10F9, /*U+10F9*/ /**/
+  0x10FA, /*U+10FA*/ /**/
+  0x10FB, /*U+10FB*/ /**/
+  0x10FC, /*U+10FC*/ /**/
+  0x10FD, /*U+10FD*/ /**/
+  0x10FE, /*U+10FE*/ /**/
+  0x10FF, /*U+10FF*/ /**/
+};
+
+static const u_int16_t lower_table_5[512] = {
+  0x1E01, /*U+1E00*/ /*LATIN CAPITAL LETTER A WITH RING BELOW*/
+  0x1E01, /*U+1E01*/ /**/
+  0x1E03, /*U+1E02*/ /*LATIN CAPITAL LETTER B WITH DOT ABOVE*/
+  0x1E03, /*U+1E03*/ /**/
+  0x1E05, /*U+1E04*/ /*LATIN CAPITAL LETTER B WITH DOT BELOW*/
+  0x1E05, /*U+1E05*/ /**/
+  0x1E07, /*U+1E06*/ /*LATIN CAPITAL LETTER B WITH LINE BELOW*/
+  0x1E07, /*U+1E07*/ /**/
+  0x1E09, /*U+1E08*/ /*LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE*/
+  0x1E09, /*U+1E09*/ /**/
+  0x1E0B, /*U+1E0A*/ /*LATIN CAPITAL LETTER D WITH DOT ABOVE*/
+  0x1E0B, /*U+1E0B*/ /**/
+  0x1E0D, /*U+1E0C*/ /*LATIN CAPITAL LETTER D WITH DOT BELOW*/
+  0x1E0D, /*U+1E0D*/ /**/
+  0x1E0F, /*U+1E0E*/ /*LATIN CAPITAL LETTER D WITH LINE BELOW*/
+  0x1E0F, /*U+1E0F*/ /**/
+  0x1E11, /*U+1E10*/ /*LATIN CAPITAL LETTER D WITH CEDILLA*/
+  0x1E11, /*U+1E11*/ /**/
+  0x1E13, /*U+1E12*/ /*LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW*/
+  0x1E13, /*U+1E13*/ /**/
+  0x1E15, /*U+1E14*/ /*LATIN CAPITAL LETTER E WITH MACRON AND GRAVE*/
+  0x1E15, /*U+1E15*/ /**/
+  0x1E17, /*U+1E16*/ /*LATIN CAPITAL LETTER E WITH MACRON AND ACUTE*/
+  0x1E17, /*U+1E17*/ /**/
+  0x1E19, /*U+1E18*/ /*LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW*/
+  0x1E19, /*U+1E19*/ /**/
+  0x1E1B, /*U+1E1A*/ /*LATIN CAPITAL LETTER E WITH TILDE BELOW*/
+  0x1E1B, /*U+1E1B*/ /**/
+  0x1E1D, /*U+1E1C*/ /*LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE*/
+  0x1E1D, /*U+1E1D*/ /**/
+  0x1E1F, /*U+1E1E*/ /*LATIN CAPITAL LETTER F WITH DOT ABOVE*/
+  0x1E1F, /*U+1E1F*/ /**/
+  0x1E21, /*U+1E20*/ /*LATIN CAPITAL LETTER G WITH MACRON*/
+  0x1E21, /*U+1E21*/ /**/
+  0x1E23, /*U+1E22*/ /*LATIN CAPITAL LETTER H WITH DOT ABOVE*/
+  0x1E23, /*U+1E23*/ /**/
+  0x1E25, /*U+1E24*/ /*LATIN CAPITAL LETTER H WITH DOT BELOW*/
+  0x1E25, /*U+1E25*/ /**/
+  0x1E27, /*U+1E26*/ /*LATIN CAPITAL LETTER H WITH DIAERESIS*/
+  0x1E27, /*U+1E27*/ /**/
+  0x1E29, /*U+1E28*/ /*LATIN CAPITAL LETTER H WITH CEDILLA*/
+  0x1E29, /*U+1E29*/ /**/
+  0x1E2B, /*U+1E2A*/ /*LATIN CAPITAL LETTER H WITH BREVE BELOW*/
+  0x1E2B, /*U+1E2B*/ /**/
+  0x1E2D, /*U+1E2C*/ /*LATIN CAPITAL LETTER I WITH TILDE BELOW*/
+  0x1E2D, /*U+1E2D*/ /**/
+  0x1E2F, /*U+1E2E*/ /*LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE*/
+  0x1E2F, /*U+1E2F*/ /**/
+  0x1E31, /*U+1E30*/ /*LATIN CAPITAL LETTER K WITH ACUTE*/
+  0x1E31, /*U+1E31*/ /**/
+  0x1E33, /*U+1E32*/ /*LATIN CAPITAL LETTER K WITH DOT BELOW*/
+  0x1E33, /*U+1E33*/ /**/
+  0x1E35, /*U+1E34*/ /*LATIN CAPITAL LETTER K WITH LINE BELOW*/
+  0x1E35, /*U+1E35*/ /**/
+  0x1E37, /*U+1E36*/ /*LATIN CAPITAL LETTER L WITH DOT BELOW*/
+  0x1E37, /*U+1E37*/ /**/
+  0x1E39, /*U+1E38*/ /*LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON*/
+  0x1E39, /*U+1E39*/ /**/
+  0x1E3B, /*U+1E3A*/ /*LATIN CAPITAL LETTER L WITH LINE BELOW*/
+  0x1E3B, /*U+1E3B*/ /**/
+  0x1E3D, /*U+1E3C*/ /*LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW*/
+  0x1E3D, /*U+1E3D*/ /**/
+  0x1E3F, /*U+1E3E*/ /*LATIN CAPITAL LETTER M WITH ACUTE*/
+  0x1E3F, /*U+1E3F*/ /**/
+  0x1E41, /*U+1E40*/ /*LATIN CAPITAL LETTER M WITH DOT ABOVE*/
+  0x1E41, /*U+1E41*/ /**/
+  0x1E43, /*U+1E42*/ /*LATIN CAPITAL LETTER M WITH DOT BELOW*/
+  0x1E43, /*U+1E43*/ /**/
+  0x1E45, /*U+1E44*/ /*LATIN CAPITAL LETTER N WITH DOT ABOVE*/
+  0x1E45, /*U+1E45*/ /**/
+  0x1E47, /*U+1E46*/ /*LATIN CAPITAL LETTER N WITH DOT BELOW*/
+  0x1E47, /*U+1E47*/ /**/
+  0x1E49, /*U+1E48*/ /*LATIN CAPITAL LETTER N WITH LINE BELOW*/
+  0x1E49, /*U+1E49*/ /**/
+  0x1E4B, /*U+1E4A*/ /*LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW*/
+  0x1E4B, /*U+1E4B*/ /**/
+  0x1E4D, /*U+1E4C*/ /*LATIN CAPITAL LETTER O WITH TILDE AND ACUTE*/
+  0x1E4D, /*U+1E4D*/ /**/
+  0x1E4F, /*U+1E4E*/ /*LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS*/
+  0x1E4F, /*U+1E4F*/ /**/
+  0x1E51, /*U+1E50*/ /*LATIN CAPITAL LETTER O WITH MACRON AND GRAVE*/
+  0x1E51, /*U+1E51*/ /**/
+  0x1E53, /*U+1E52*/ /*LATIN CAPITAL LETTER O WITH MACRON AND ACUTE*/
+  0x1E53, /*U+1E53*/ /**/
+  0x1E55, /*U+1E54*/ /*LATIN CAPITAL LETTER P WITH ACUTE*/
+  0x1E55, /*U+1E55*/ /**/
+  0x1E57, /*U+1E56*/ /*LATIN CAPITAL LETTER P WITH DOT ABOVE*/
+  0x1E57, /*U+1E57*/ /**/
+  0x1E59, /*U+1E58*/ /*LATIN CAPITAL LETTER R WITH DOT ABOVE*/
+  0x1E59, /*U+1E59*/ /**/
+  0x1E5B, /*U+1E5A*/ /*LATIN CAPITAL LETTER R WITH DOT BELOW*/
+  0x1E5B, /*U+1E5B*/ /**/
+  0x1E5D, /*U+1E5C*/ /*LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON*/
+  0x1E5D, /*U+1E5D*/ /**/
+  0x1E5F, /*U+1E5E*/ /*LATIN CAPITAL LETTER R WITH LINE BELOW*/
+  0x1E5F, /*U+1E5F*/ /**/
+  0x1E61, /*U+1E60*/ /*LATIN CAPITAL LETTER S WITH DOT ABOVE*/
+  0x1E61, /*U+1E61*/ /**/
+  0x1E63, /*U+1E62*/ /*LATIN CAPITAL LETTER S WITH DOT BELOW*/
+  0x1E63, /*U+1E63*/ /**/
+  0x1E65, /*U+1E64*/ /*LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE*/
+  0x1E65, /*U+1E65*/ /**/
+  0x1E67, /*U+1E66*/ /*LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE*/
+  0x1E67, /*U+1E67*/ /**/
+  0x1E69, /*U+1E68*/ /*LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE*/
+  0x1E69, /*U+1E69*/ /**/
+  0x1E6B, /*U+1E6A*/ /*LATIN CAPITAL LETTER T WITH DOT ABOVE*/
+  0x1E6B, /*U+1E6B*/ /**/
+  0x1E6D, /*U+1E6C*/ /*LATIN CAPITAL LETTER T WITH DOT BELOW*/
+  0x1E6D, /*U+1E6D*/ /**/
+  0x1E6F, /*U+1E6E*/ /*LATIN CAPITAL LETTER T WITH LINE BELOW*/
+  0x1E6F, /*U+1E6F*/ /**/
+  0x1E71, /*U+1E70*/ /*LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW*/
+  0x1E71, /*U+1E71*/ /**/
+  0x1E73, /*U+1E72*/ /*LATIN CAPITAL LETTER U WITH DIAERESIS BELOW*/
+  0x1E73, /*U+1E73*/ /**/
+  0x1E75, /*U+1E74*/ /*LATIN CAPITAL LETTER U WITH TILDE BELOW*/
+  0x1E75, /*U+1E75*/ /**/
+  0x1E77, /*U+1E76*/ /*LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW*/
+  0x1E77, /*U+1E77*/ /**/
+  0x1E79, /*U+1E78*/ /*LATIN CAPITAL LETTER U WITH TILDE AND ACUTE*/
+  0x1E79, /*U+1E79*/ /**/
+  0x1E7B, /*U+1E7A*/ /*LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS*/
+  0x1E7B, /*U+1E7B*/ /**/
+  0x1E7D, /*U+1E7C*/ /*LATIN CAPITAL LETTER V WITH TILDE*/
+  0x1E7D, /*U+1E7D*/ /**/
+  0x1E7F, /*U+1E7E*/ /*LATIN CAPITAL LETTER V WITH DOT BELOW*/
+  0x1E7F, /*U+1E7F*/ /**/
+  0x1E81, /*U+1E80*/ /*LATIN CAPITAL LETTER W WITH GRAVE*/
+  0x1E81, /*U+1E81*/ /**/
+  0x1E83, /*U+1E82*/ /*LATIN CAPITAL LETTER W WITH ACUTE*/
+  0x1E83, /*U+1E83*/ /**/
+  0x1E85, /*U+1E84*/ /*LATIN CAPITAL LETTER W WITH DIAERESIS*/
+  0x1E85, /*U+1E85*/ /**/
+  0x1E87, /*U+1E86*/ /*LATIN CAPITAL LETTER W WITH DOT ABOVE*/
+  0x1E87, /*U+1E87*/ /**/
+  0x1E89, /*U+1E88*/ /*LATIN CAPITAL LETTER W WITH DOT BELOW*/
+  0x1E89, /*U+1E89*/ /**/
+  0x1E8B, /*U+1E8A*/ /*LATIN CAPITAL LETTER X WITH DOT ABOVE*/
+  0x1E8B, /*U+1E8B*/ /**/
+  0x1E8D, /*U+1E8C*/ /*LATIN CAPITAL LETTER X WITH DIAERESIS*/
+  0x1E8D, /*U+1E8D*/ /**/
+  0x1E8F, /*U+1E8E*/ /*LATIN CAPITAL LETTER Y WITH DOT ABOVE*/
+  0x1E8F, /*U+1E8F*/ /**/
+  0x1E91, /*U+1E90*/ /*LATIN CAPITAL LETTER Z WITH CIRCUMFLEX*/
+  0x1E91, /*U+1E91*/ /**/
+  0x1E93, /*U+1E92*/ /*LATIN CAPITAL LETTER Z WITH DOT BELOW*/
+  0x1E93, /*U+1E93*/ /**/
+  0x1E95, /*U+1E94*/ /*LATIN CAPITAL LETTER Z WITH LINE BELOW*/
+  0x1E95, /*U+1E95*/ /**/
+  0x1E96, /*U+1E96*/ /**/
+  0x1E97, /*U+1E97*/ /**/
+  0x1E98, /*U+1E98*/ /**/
+  0x1E99, /*U+1E99*/ /**/
+  0x1E9A, /*U+1E9A*/ /**/
+  0x1E9B, /*U+1E9B*/ /**/
+  0x1E9C, /*U+1E9C*/ /**/
+  0x1E9D, /*U+1E9D*/ /**/
+  0x00DF, /*U+1E9E*/ /*LATIN CAPITAL LETTER SHARP S*/
+  0x1E9F, /*U+1E9F*/ /**/
+  0x1EA1, /*U+1EA0*/ /*LATIN CAPITAL LETTER A WITH DOT BELOW*/
+  0x1EA1, /*U+1EA1*/ /**/
+  0x1EA3, /*U+1EA2*/ /*LATIN CAPITAL LETTER A WITH HOOK ABOVE*/
+  0x1EA3, /*U+1EA3*/ /**/
+  0x1EA5, /*U+1EA4*/ /*LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE*/
+  0x1EA5, /*U+1EA5*/ /**/
+  0x1EA7, /*U+1EA6*/ /*LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE*/
+  0x1EA7, /*U+1EA7*/ /**/
+  0x1EA9, /*U+1EA8*/ /*LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE*/
+  0x1EA9, /*U+1EA9*/ /**/
+  0x1EAB, /*U+1EAA*/ /*LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE*/
+  0x1EAB, /*U+1EAB*/ /**/
+  0x1EAD, /*U+1EAC*/ /*LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW*/
+  0x1EAD, /*U+1EAD*/ /**/
+  0x1EAF, /*U+1EAE*/ /*LATIN CAPITAL LETTER A WITH BREVE AND ACUTE*/
+  0x1EAF, /*U+1EAF*/ /**/
+  0x1EB1, /*U+1EB0*/ /*LATIN CAPITAL LETTER A WITH BREVE AND GRAVE*/
+  0x1EB1, /*U+1EB1*/ /**/
+  0x1EB3, /*U+1EB2*/ /*LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE*/
+  0x1EB3, /*U+1EB3*/ /**/
+  0x1EB5, /*U+1EB4*/ /*LATIN CAPITAL LETTER A WITH BREVE AND TILDE*/
+  0x1EB5, /*U+1EB5*/ /**/
+  0x1EB7, /*U+1EB6*/ /*LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW*/
+  0x1EB7, /*U+1EB7*/ /**/
+  0x1EB9, /*U+1EB8*/ /*LATIN CAPITAL LETTER E WITH DOT BELOW*/
+  0x1EB9, /*U+1EB9*/ /**/
+  0x1EBB, /*U+1EBA*/ /*LATIN CAPITAL LETTER E WITH HOOK ABOVE*/
+  0x1EBB, /*U+1EBB*/ /**/
+  0x1EBD, /*U+1EBC*/ /*LATIN CAPITAL LETTER E WITH TILDE*/
+  0x1EBD, /*U+1EBD*/ /**/
+  0x1EBF, /*U+1EBE*/ /*LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE*/
+  0x1EBF, /*U+1EBF*/ /**/
+  0x1EC1, /*U+1EC0*/ /*LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE*/
+  0x1EC1, /*U+1EC1*/ /**/
+  0x1EC3, /*U+1EC2*/ /*LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE*/
+  0x1EC3, /*U+1EC3*/ /**/
+  0x1EC5, /*U+1EC4*/ /*LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE*/
+  0x1EC5, /*U+1EC5*/ /**/
+  0x1EC7, /*U+1EC6*/ /*LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW*/
+  0x1EC7, /*U+1EC7*/ /**/
+  0x1EC9, /*U+1EC8*/ /*LATIN CAPITAL LETTER I WITH HOOK ABOVE*/
+  0x1EC9, /*U+1EC9*/ /**/
+  0x1ECB, /*U+1ECA*/ /*LATIN CAPITAL LETTER I WITH DOT BELOW*/
+  0x1ECB, /*U+1ECB*/ /**/
+  0x1ECD, /*U+1ECC*/ /*LATIN CAPITAL LETTER O WITH DOT BELOW*/
+  0x1ECD, /*U+1ECD*/ /**/
+  0x1ECF, /*U+1ECE*/ /*LATIN CAPITAL LETTER O WITH HOOK ABOVE*/
+  0x1ECF, /*U+1ECF*/ /**/
+  0x1ED1, /*U+1ED0*/ /*LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE*/
+  0x1ED1, /*U+1ED1*/ /**/
+  0x1ED3, /*U+1ED2*/ /*LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE*/
+  0x1ED3, /*U+1ED3*/ /**/
+  0x1ED5, /*U+1ED4*/ /*LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE*/
+  0x1ED5, /*U+1ED5*/ /**/
+  0x1ED7, /*U+1ED6*/ /*LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE*/
+  0x1ED7, /*U+1ED7*/ /**/
+  0x1ED9, /*U+1ED8*/ /*LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW*/
+  0x1ED9, /*U+1ED9*/ /**/
+  0x1EDB, /*U+1EDA*/ /*LATIN CAPITAL LETTER O WITH HORN AND ACUTE*/
+  0x1EDB, /*U+1EDB*/ /**/
+  0x1EDD, /*U+1EDC*/ /*LATIN CAPITAL LETTER O WITH HORN AND GRAVE*/
+  0x1EDD, /*U+1EDD*/ /**/
+  0x1EDF, /*U+1EDE*/ /*LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE*/
+  0x1EDF, /*U+1EDF*/ /**/
+  0x1EE1, /*U+1EE0*/ /*LATIN CAPITAL LETTER O WITH HORN AND TILDE*/
+  0x1EE1, /*U+1EE1*/ /**/
+  0x1EE3, /*U+1EE2*/ /*LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW*/
+  0x1EE3, /*U+1EE3*/ /**/
+  0x1EE5, /*U+1EE4*/ /*LATIN CAPITAL LETTER U WITH DOT BELOW*/
+  0x1EE5, /*U+1EE5*/ /**/
+  0x1EE7, /*U+1EE6*/ /*LATIN CAPITAL LETTER U WITH HOOK ABOVE*/
+  0x1EE7, /*U+1EE7*/ /**/
+  0x1EE9, /*U+1EE8*/ /*LATIN CAPITAL LETTER U WITH HORN AND ACUTE*/
+  0x1EE9, /*U+1EE9*/ /**/
+  0x1EEB, /*U+1EEA*/ /*LATIN CAPITAL LETTER U WITH HORN AND GRAVE*/
+  0x1EEB, /*U+1EEB*/ /**/
+  0x1EED, /*U+1EEC*/ /*LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE*/
+  0x1EED, /*U+1EED*/ /**/
+  0x1EEF, /*U+1EEE*/ /*LATIN CAPITAL LETTER U WITH HORN AND TILDE*/
+  0x1EEF, /*U+1EEF*/ /**/
+  0x1EF1, /*U+1EF0*/ /*LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW*/
+  0x1EF1, /*U+1EF1*/ /**/
+  0x1EF3, /*U+1EF2*/ /*LATIN CAPITAL LETTER Y WITH GRAVE*/
+  0x1EF3, /*U+1EF3*/ /**/
+  0x1EF5, /*U+1EF4*/ /*LATIN CAPITAL LETTER Y WITH DOT BELOW*/
+  0x1EF5, /*U+1EF5*/ /**/
+  0x1EF7, /*U+1EF6*/ /*LATIN CAPITAL LETTER Y WITH HOOK ABOVE*/
+  0x1EF7, /*U+1EF7*/ /**/
+  0x1EF9, /*U+1EF8*/ /*LATIN CAPITAL LETTER Y WITH TILDE*/
+  0x1EF9, /*U+1EF9*/ /**/
+  0x1EFB, /*U+1EFA*/ /*LATIN CAPITAL LETTER MIDDLE-WELSH LL*/
+  0x1EFB, /*U+1EFB*/ /**/
+  0x1EFD, /*U+1EFC*/ /*LATIN CAPITAL LETTER MIDDLE-WELSH V*/
+  0x1EFD, /*U+1EFD*/ /**/
+  0x1EFF, /*U+1EFE*/ /*LATIN CAPITAL LETTER Y WITH LOOP*/
+  0x1EFF, /*U+1EFF*/ /**/
+  0x1F00, /*U+1F00*/ /**/
+  0x1F01, /*U+1F01*/ /**/
+  0x1F02, /*U+1F02*/ /**/
+  0x1F03, /*U+1F03*/ /**/
+  0x1F04, /*U+1F04*/ /**/
+  0x1F05, /*U+1F05*/ /**/
+  0x1F06, /*U+1F06*/ /**/
+  0x1F07, /*U+1F07*/ /**/
+  0x1F00, /*U+1F08*/ /*GREEK CAPITAL LETTER ALPHA WITH PSILI*/
+  0x1F01, /*U+1F09*/ /*GREEK CAPITAL LETTER ALPHA WITH DASIA*/
+  0x1F02, /*U+1F0A*/ /*GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA*/
+  0x1F03, /*U+1F0B*/ /*GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA*/
+  0x1F04, /*U+1F0C*/ /*GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA*/
+  0x1F05, /*U+1F0D*/ /*GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA*/
+  0x1F06, /*U+1F0E*/ /*GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI*/
+  0x1F07, /*U+1F0F*/ /*GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI*/
+  0x1F10, /*U+1F10*/ /**/
+  0x1F11, /*U+1F11*/ /**/
+  0x1F12, /*U+1F12*/ /**/
+  0x1F13, /*U+1F13*/ /**/
+  0x1F14, /*U+1F14*/ /**/
+  0x1F15, /*U+1F15*/ /**/
+  0x1F16, /*U+1F16*/ /**/
+  0x1F17, /*U+1F17*/ /**/
+  0x1F10, /*U+1F18*/ /*GREEK CAPITAL LETTER EPSILON WITH PSILI*/
+  0x1F11, /*U+1F19*/ /*GREEK CAPITAL LETTER EPSILON WITH DASIA*/
+  0x1F12, /*U+1F1A*/ /*GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA*/
+  0x1F13, /*U+1F1B*/ /*GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA*/
+  0x1F14, /*U+1F1C*/ /*GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA*/
+  0x1F15, /*U+1F1D*/ /*GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA*/
+  0x1F1E, /*U+1F1E*/ /**/
+  0x1F1F, /*U+1F1F*/ /**/
+  0x1F20, /*U+1F20*/ /**/
+  0x1F21, /*U+1F21*/ /**/
+  0x1F22, /*U+1F22*/ /**/
+  0x1F23, /*U+1F23*/ /**/
+  0x1F24, /*U+1F24*/ /**/
+  0x1F25, /*U+1F25*/ /**/
+  0x1F26, /*U+1F26*/ /**/
+  0x1F27, /*U+1F27*/ /**/
+  0x1F20, /*U+1F28*/ /*GREEK CAPITAL LETTER ETA WITH PSILI*/
+  0x1F21, /*U+1F29*/ /*GREEK CAPITAL LETTER ETA WITH DASIA*/
+  0x1F22, /*U+1F2A*/ /*GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA*/
+  0x1F23, /*U+1F2B*/ /*GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA*/
+  0x1F24, /*U+1F2C*/ /*GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA*/
+  0x1F25, /*U+1F2D*/ /*GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA*/
+  0x1F26, /*U+1F2E*/ /*GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI*/
+  0x1F27, /*U+1F2F*/ /*GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI*/
+  0x1F30, /*U+1F30*/ /**/
+  0x1F31, /*U+1F31*/ /**/
+  0x1F32, /*U+1F32*/ /**/
+  0x1F33, /*U+1F33*/ /**/
+  0x1F34, /*U+1F34*/ /**/
+  0x1F35, /*U+1F35*/ /**/
+  0x1F36, /*U+1F36*/ /**/
+  0x1F37, /*U+1F37*/ /**/
+  0x1F30, /*U+1F38*/ /*GREEK CAPITAL LETTER IOTA WITH PSILI*/
+  0x1F31, /*U+1F39*/ /*GREEK CAPITAL LETTER IOTA WITH DASIA*/
+  0x1F32, /*U+1F3A*/ /*GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA*/
+  0x1F33, /*U+1F3B*/ /*GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA*/
+  0x1F34, /*U+1F3C*/ /*GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA*/
+  0x1F35, /*U+1F3D*/ /*GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA*/
+  0x1F36, /*U+1F3E*/ /*GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI*/
+  0x1F37, /*U+1F3F*/ /*GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI*/
+  0x1F40, /*U+1F40*/ /**/
+  0x1F41, /*U+1F41*/ /**/
+  0x1F42, /*U+1F42*/ /**/
+  0x1F43, /*U+1F43*/ /**/
+  0x1F44, /*U+1F44*/ /**/
+  0x1F45, /*U+1F45*/ /**/
+  0x1F46, /*U+1F46*/ /**/
+  0x1F47, /*U+1F47*/ /**/
+  0x1F40, /*U+1F48*/ /*GREEK CAPITAL LETTER OMICRON WITH PSILI*/
+  0x1F41, /*U+1F49*/ /*GREEK CAPITAL LETTER OMICRON WITH DASIA*/
+  0x1F42, /*U+1F4A*/ /*GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA*/
+  0x1F43, /*U+1F4B*/ /*GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA*/
+  0x1F44, /*U+1F4C*/ /*GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA*/
+  0x1F45, /*U+1F4D*/ /*GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA*/
+  0x1F4E, /*U+1F4E*/ /**/
+  0x1F4F, /*U+1F4F*/ /**/
+  0x1F50, /*U+1F50*/ /**/
+  0x1F51, /*U+1F51*/ /**/
+  0x1F52, /*U+1F52*/ /**/
+  0x1F53, /*U+1F53*/ /**/
+  0x1F54, /*U+1F54*/ /**/
+  0x1F55, /*U+1F55*/ /**/
+  0x1F56, /*U+1F56*/ /**/
+  0x1F57, /*U+1F57*/ /**/
+  0x1F58, /*U+1F58*/ /**/
+  0x1F51, /*U+1F59*/ /*GREEK CAPITAL LETTER UPSILON WITH DASIA*/
+  0x1F5A, /*U+1F5A*/ /**/
+  0x1F53, /*U+1F5B*/ /*GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA*/
+  0x1F5C, /*U+1F5C*/ /**/
+  0x1F55, /*U+1F5D*/ /*GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA*/
+  0x1F5E, /*U+1F5E*/ /**/
+  0x1F57, /*U+1F5F*/ /*GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI*/
+  0x1F60, /*U+1F60*/ /**/
+  0x1F61, /*U+1F61*/ /**/
+  0x1F62, /*U+1F62*/ /**/
+  0x1F63, /*U+1F63*/ /**/
+  0x1F64, /*U+1F64*/ /**/
+  0x1F65, /*U+1F65*/ /**/
+  0x1F66, /*U+1F66*/ /**/
+  0x1F67, /*U+1F67*/ /**/
+  0x1F60, /*U+1F68*/ /*GREEK CAPITAL LETTER OMEGA WITH PSILI*/
+  0x1F61, /*U+1F69*/ /*GREEK CAPITAL LETTER OMEGA WITH DASIA*/
+  0x1F62, /*U+1F6A*/ /*GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA*/
+  0x1F63, /*U+1F6B*/ /*GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA*/
+  0x1F64, /*U+1F6C*/ /*GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA*/
+  0x1F65, /*U+1F6D*/ /*GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA*/
+  0x1F66, /*U+1F6E*/ /*GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI*/
+  0x1F67, /*U+1F6F*/ /*GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI*/
+  0x1F70, /*U+1F70*/ /**/
+  0x1F71, /*U+1F71*/ /**/
+  0x1F72, /*U+1F72*/ /**/
+  0x1F73, /*U+1F73*/ /**/
+  0x1F74, /*U+1F74*/ /**/
+  0x1F75, /*U+1F75*/ /**/
+  0x1F76, /*U+1F76*/ /**/
+  0x1F77, /*U+1F77*/ /**/
+  0x1F78, /*U+1F78*/ /**/
+  0x1F79, /*U+1F79*/ /**/
+  0x1F7A, /*U+1F7A*/ /**/
+  0x1F7B, /*U+1F7B*/ /**/
+  0x1F7C, /*U+1F7C*/ /**/
+  0x1F7D, /*U+1F7D*/ /**/
+  0x1F7E, /*U+1F7E*/ /**/
+  0x1F7F, /*U+1F7F*/ /**/
+  0x1F80, /*U+1F80*/ /**/
+  0x1F81, /*U+1F81*/ /**/
+  0x1F82, /*U+1F82*/ /**/
+  0x1F83, /*U+1F83*/ /**/
+  0x1F84, /*U+1F84*/ /**/
+  0x1F85, /*U+1F85*/ /**/
+  0x1F86, /*U+1F86*/ /**/
+  0x1F87, /*U+1F87*/ /**/
+  0x1F80, /*U+1F88*/ /*GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI*/
+  0x1F81, /*U+1F89*/ /*GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI*/
+  0x1F82, /*U+1F8A*/ /*GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI*/
+  0x1F83, /*U+1F8B*/ /*GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI*/
+  0x1F84, /*U+1F8C*/ /*GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI*/
+  0x1F85, /*U+1F8D*/ /*GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI*/
+  0x1F86, /*U+1F8E*/ /*GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI*/
+  0x1F87, /*U+1F8F*/ /*GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI*/
+  0x1F90, /*U+1F90*/ /**/
+  0x1F91, /*U+1F91*/ /**/
+  0x1F92, /*U+1F92*/ /**/
+  0x1F93, /*U+1F93*/ /**/
+  0x1F94, /*U+1F94*/ /**/
+  0x1F95, /*U+1F95*/ /**/
+  0x1F96, /*U+1F96*/ /**/
+  0x1F97, /*U+1F97*/ /**/
+  0x1F90, /*U+1F98*/ /*GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI*/
+  0x1F91, /*U+1F99*/ /*GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI*/
+  0x1F92, /*U+1F9A*/ /*GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI*/
+  0x1F93, /*U+1F9B*/ /*GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI*/
+  0x1F94, /*U+1F9C*/ /*GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI*/
+  0x1F95, /*U+1F9D*/ /*GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI*/
+  0x1F96, /*U+1F9E*/ /*GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI*/
+  0x1F97, /*U+1F9F*/ /*GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI*/
+  0x1FA0, /*U+1FA0*/ /**/
+  0x1FA1, /*U+1FA1*/ /**/
+  0x1FA2, /*U+1FA2*/ /**/
+  0x1FA3, /*U+1FA3*/ /**/
+  0x1FA4, /*U+1FA4*/ /**/
+  0x1FA5, /*U+1FA5*/ /**/
+  0x1FA6, /*U+1FA6*/ /**/
+  0x1FA7, /*U+1FA7*/ /**/
+  0x1FA0, /*U+1FA8*/ /*GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI*/
+  0x1FA1, /*U+1FA9*/ /*GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI*/
+  0x1FA2, /*U+1FAA*/ /*GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI*/
+  0x1FA3, /*U+1FAB*/ /*GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI*/
+  0x1FA4, /*U+1FAC*/ /*GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI*/
+  0x1FA5, /*U+1FAD*/ /*GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI*/
+  0x1FA6, /*U+1FAE*/ /*GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI*/
+  0x1FA7, /*U+1FAF*/ /*GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI*/
+  0x1FB0, /*U+1FB0*/ /**/
+  0x1FB1, /*U+1FB1*/ /**/
+  0x1FB2, /*U+1FB2*/ /**/
+  0x1FB3, /*U+1FB3*/ /**/
+  0x1FB4, /*U+1FB4*/ /**/
+  0x1FB5, /*U+1FB5*/ /**/
+  0x1FB6, /*U+1FB6*/ /**/
+  0x1FB7, /*U+1FB7*/ /**/
+  0x1FB0, /*U+1FB8*/ /*GREEK CAPITAL LETTER ALPHA WITH VRACHY*/
+  0x1FB1, /*U+1FB9*/ /*GREEK CAPITAL LETTER ALPHA WITH MACRON*/
+  0x1F70, /*U+1FBA*/ /*GREEK CAPITAL LETTER ALPHA WITH VARIA*/
+  0x1F71, /*U+1FBB*/ /*GREEK CAPITAL LETTER ALPHA WITH OXIA*/
+  0x1FB3, /*U+1FBC*/ /*GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI*/
+  0x1FBD, /*U+1FBD*/ /**/
+  0x1FBE, /*U+1FBE*/ /**/
+  0x1FBF, /*U+1FBF*/ /**/
+  0x1FC0, /*U+1FC0*/ /**/
+  0x1FC1, /*U+1FC1*/ /**/
+  0x1FC2, /*U+1FC2*/ /**/
+  0x1FC3, /*U+1FC3*/ /**/
+  0x1FC4, /*U+1FC4*/ /**/
+  0x1FC5, /*U+1FC5*/ /**/
+  0x1FC6, /*U+1FC6*/ /**/
+  0x1FC7, /*U+1FC7*/ /**/
+  0x1F72, /*U+1FC8*/ /*GREEK CAPITAL LETTER EPSILON WITH VARIA*/
+  0x1F73, /*U+1FC9*/ /*GREEK CAPITAL LETTER EPSILON WITH OXIA*/
+  0x1F74, /*U+1FCA*/ /*GREEK CAPITAL LETTER ETA WITH VARIA*/
+  0x1F75, /*U+1FCB*/ /*GREEK CAPITAL LETTER ETA WITH OXIA*/
+  0x1FC3, /*U+1FCC*/ /*GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI*/
+  0x1FCD, /*U+1FCD*/ /**/
+  0x1FCE, /*U+1FCE*/ /**/
+  0x1FCF, /*U+1FCF*/ /**/
+  0x1FD0, /*U+1FD0*/ /**/
+  0x1FD1, /*U+1FD1*/ /**/
+  0x1FD2, /*U+1FD2*/ /**/
+  0x1FD3, /*U+1FD3*/ /**/
+  0x1FD4, /*U+1FD4*/ /**/
+  0x1FD5, /*U+1FD5*/ /**/
+  0x1FD6, /*U+1FD6*/ /**/
+  0x1FD7, /*U+1FD7*/ /**/
+  0x1FD0, /*U+1FD8*/ /*GREEK CAPITAL LETTER IOTA WITH VRACHY*/
+  0x1FD1, /*U+1FD9*/ /*GREEK CAPITAL LETTER IOTA WITH MACRON*/
+  0x1F76, /*U+1FDA*/ /*GREEK CAPITAL LETTER IOTA WITH VARIA*/
+  0x1F77, /*U+1FDB*/ /*GREEK CAPITAL LETTER IOTA WITH OXIA*/
+  0x1FDC, /*U+1FDC*/ /**/
+  0x1FDD, /*U+1FDD*/ /**/
+  0x1FDE, /*U+1FDE*/ /**/
+  0x1FDF, /*U+1FDF*/ /**/
+  0x1FE0, /*U+1FE0*/ /**/
+  0x1FE1, /*U+1FE1*/ /**/
+  0x1FE2, /*U+1FE2*/ /**/
+  0x1FE3, /*U+1FE3*/ /**/
+  0x1FE4, /*U+1FE4*/ /**/
+  0x1FE5, /*U+1FE5*/ /**/
+  0x1FE6, /*U+1FE6*/ /**/
+  0x1FE7, /*U+1FE7*/ /**/
+  0x1FE0, /*U+1FE8*/ /*GREEK CAPITAL LETTER UPSILON WITH VRACHY*/
+  0x1FE1, /*U+1FE9*/ /*GREEK CAPITAL LETTER UPSILON WITH MACRON*/
+  0x1F7A, /*U+1FEA*/ /*GREEK CAPITAL LETTER UPSILON WITH VARIA*/
+  0x1F7B, /*U+1FEB*/ /*GREEK CAPITAL LETTER UPSILON WITH OXIA*/
+  0x1FE5, /*U+1FEC*/ /*GREEK CAPITAL LETTER RHO WITH DASIA*/
+  0x1FED, /*U+1FED*/ /**/
+  0x1FEE, /*U+1FEE*/ /**/
+  0x1FEF, /*U+1FEF*/ /**/
+  0x1FF0, /*U+1FF0*/ /**/
+  0x1FF1, /*U+1FF1*/ /**/
+  0x1FF2, /*U+1FF2*/ /**/
+  0x1FF3, /*U+1FF3*/ /**/
+  0x1FF4, /*U+1FF4*/ /**/
+  0x1FF5, /*U+1FF5*/ /**/
+  0x1FF6, /*U+1FF6*/ /**/
+  0x1FF7, /*U+1FF7*/ /**/
+  0x1F78, /*U+1FF8*/ /*GREEK CAPITAL LETTER OMICRON WITH VARIA*/
+  0x1F79, /*U+1FF9*/ /*GREEK CAPITAL LETTER OMICRON WITH OXIA*/
+  0x1F7C, /*U+1FFA*/ /*GREEK CAPITAL LETTER OMEGA WITH VARIA*/
+  0x1F7D, /*U+1FFB*/ /*GREEK CAPITAL LETTER OMEGA WITH OXIA*/
+  0x1FF3, /*U+1FFC*/ /*GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI*/
+  0x1FFD, /*U+1FFD*/ /**/
+  0x1FFE, /*U+1FFE*/ /**/
+  0x1FFF, /*U+1FFF*/ /**/
+};
+
+static const u_int16_t lower_table_6[192] = {
+  0x2100, /*U+2100*/ /**/
+  0x2101, /*U+2101*/ /**/
+  0x2102, /*U+2102*/ /**/
+  0x2103, /*U+2103*/ /**/
+  0x2104, /*U+2104*/ /**/
+  0x2105, /*U+2105*/ /**/
+  0x2106, /*U+2106*/ /**/
+  0x2107, /*U+2107*/ /**/
+  0x2108, /*U+2108*/ /**/
+  0x2109, /*U+2109*/ /**/
+  0x210A, /*U+210A*/ /**/
+  0x210B, /*U+210B*/ /**/
+  0x210C, /*U+210C*/ /**/
+  0x210D, /*U+210D*/ /**/
+  0x210E, /*U+210E*/ /**/
+  0x210F, /*U+210F*/ /**/
+  0x2110, /*U+2110*/ /**/
+  0x2111, /*U+2111*/ /**/
+  0x2112, /*U+2112*/ /**/
+  0x2113, /*U+2113*/ /**/
+  0x2114, /*U+2114*/ /**/
+  0x2115, /*U+2115*/ /**/
+  0x2116, /*U+2116*/ /**/
+  0x2117, /*U+2117*/ /**/
+  0x2118, /*U+2118*/ /**/
+  0x2119, /*U+2119*/ /**/
+  0x211A, /*U+211A*/ /**/
+  0x211B, /*U+211B*/ /**/
+  0x211C, /*U+211C*/ /**/
+  0x211D, /*U+211D*/ /**/
+  0x211E, /*U+211E*/ /**/
+  0x211F, /*U+211F*/ /**/
+  0x2120, /*U+2120*/ /**/
+  0x2121, /*U+2121*/ /**/
+  0x2122, /*U+2122*/ /**/
+  0x2123, /*U+2123*/ /**/
+  0x2124, /*U+2124*/ /**/
+  0x2125, /*U+2125*/ /**/
+  0x03C9, /*U+2126*/ /*OHM SIGN*/
+  0x2127, /*U+2127*/ /**/
+  0x2128, /*U+2128*/ /**/
+  0x2129, /*U+2129*/ /**/
+  0x006B, /*U+212A*/ /*KELVIN SIGN*/
+  0x00E5, /*U+212B*/ /*ANGSTROM SIGN*/
+  0x212C, /*U+212C*/ /**/
+  0x212D, /*U+212D*/ /**/
+  0x212E, /*U+212E*/ /**/
+  0x212F, /*U+212F*/ /**/
+  0x2130, /*U+2130*/ /**/
+  0x2131, /*U+2131*/ /**/
+  0x214E, /*U+2132*/ /*TURNED CAPITAL F*/
+  0x2133, /*U+2133*/ /**/
+  0x2134, /*U+2134*/ /**/
+  0x2135, /*U+2135*/ /**/
+  0x2136, /*U+2136*/ /**/
+  0x2137, /*U+2137*/ /**/
+  0x2138, /*U+2138*/ /**/
+  0x2139, /*U+2139*/ /**/
+  0x213A, /*U+213A*/ /**/
+  0x213B, /*U+213B*/ /**/
+  0x213C, /*U+213C*/ /**/
+  0x213D, /*U+213D*/ /**/
+  0x213E, /*U+213E*/ /**/
+  0x213F, /*U+213F*/ /**/
+  0x2140, /*U+2140*/ /**/
+  0x2141, /*U+2141*/ /**/
+  0x2142, /*U+2142*/ /**/
+  0x2143, /*U+2143*/ /**/
+  0x2144, /*U+2144*/ /**/
+  0x2145, /*U+2145*/ /**/
+  0x2146, /*U+2146*/ /**/
+  0x2147, /*U+2147*/ /**/
+  0x2148, /*U+2148*/ /**/
+  0x2149, /*U+2149*/ /**/
+  0x214A, /*U+214A*/ /**/
+  0x214B, /*U+214B*/ /**/
+  0x214C, /*U+214C*/ /**/
+  0x214D, /*U+214D*/ /**/
+  0x214E, /*U+214E*/ /**/
+  0x214F, /*U+214F*/ /**/
+  0x2150, /*U+2150*/ /**/
+  0x2151, /*U+2151*/ /**/
+  0x2152, /*U+2152*/ /**/
+  0x2153, /*U+2153*/ /**/
+  0x2154, /*U+2154*/ /**/
+  0x2155, /*U+2155*/ /**/
+  0x2156, /*U+2156*/ /**/
+  0x2157, /*U+2157*/ /**/
+  0x2158, /*U+2158*/ /**/
+  0x2159, /*U+2159*/ /**/
+  0x215A, /*U+215A*/ /**/
+  0x215B, /*U+215B*/ /**/
+  0x215C, /*U+215C*/ /**/
+  0x215D, /*U+215D*/ /**/
+  0x215E, /*U+215E*/ /**/
+  0x215F, /*U+215F*/ /**/
+  0x2170, /*U+2160*/ /*ROMAN NUMERAL ONE*/
+  0x2171, /*U+2161*/ /*ROMAN NUMERAL TWO*/
+  0x2172, /*U+2162*/ /*ROMAN NUMERAL THREE*/
+  0x2173, /*U+2163*/ /*ROMAN NUMERAL FOUR*/
+  0x2174, /*U+2164*/ /*ROMAN NUMERAL FIVE*/
+  0x2175, /*U+2165*/ /*ROMAN NUMERAL SIX*/
+  0x2176, /*U+2166*/ /*ROMAN NUMERAL SEVEN*/
+  0x2177, /*U+2167*/ /*ROMAN NUMERAL EIGHT*/
+  0x2178, /*U+2168*/ /*ROMAN NUMERAL NINE*/
+  0x2179, /*U+2169*/ /*ROMAN NUMERAL TEN*/
+  0x217A, /*U+216A*/ /*ROMAN NUMERAL ELEVEN*/
+  0x217B, /*U+216B*/ /*ROMAN NUMERAL TWELVE*/
+  0x217C, /*U+216C*/ /*ROMAN NUMERAL FIFTY*/
+  0x217D, /*U+216D*/ /*ROMAN NUMERAL ONE HUNDRED*/
+  0x217E, /*U+216E*/ /*ROMAN NUMERAL FIVE HUNDRED*/
+  0x217F, /*U+216F*/ /*ROMAN NUMERAL ONE THOUSAND*/
+  0x2170, /*U+2170*/ /**/
+  0x2171, /*U+2171*/ /**/
+  0x2172, /*U+2172*/ /**/
+  0x2173, /*U+2173*/ /**/
+  0x2174, /*U+2174*/ /**/
+  0x2175, /*U+2175*/ /**/
+  0x2176, /*U+2176*/ /**/
+  0x2177, /*U+2177*/ /**/
+  0x2178, /*U+2178*/ /**/
+  0x2179, /*U+2179*/ /**/
+  0x217A, /*U+217A*/ /**/
+  0x217B, /*U+217B*/ /**/
+  0x217C, /*U+217C*/ /**/
+  0x217D, /*U+217D*/ /**/
+  0x217E, /*U+217E*/ /**/
+  0x217F, /*U+217F*/ /**/
+  0x2180, /*U+2180*/ /**/
+  0x2181, /*U+2181*/ /**/
+  0x2182, /*U+2182*/ /**/
+  0x2184, /*U+2183*/ /*ROMAN NUMERAL REVERSED ONE HUNDRED*/
+  0x2184, /*U+2184*/ /**/
+  0x2185, /*U+2185*/ /**/
+  0x2186, /*U+2186*/ /**/
+  0x2187, /*U+2187*/ /**/
+  0x2188, /*U+2188*/ /**/
+  0x2189, /*U+2189*/ /**/
+  0x218A, /*U+218A*/ /**/
+  0x218B, /*U+218B*/ /**/
+  0x218C, /*U+218C*/ /**/
+  0x218D, /*U+218D*/ /**/
+  0x218E, /*U+218E*/ /**/
+  0x218F, /*U+218F*/ /**/
+  0x2190, /*U+2190*/ /**/
+  0x2191, /*U+2191*/ /**/
+  0x2192, /*U+2192*/ /**/
+  0x2193, /*U+2193*/ /**/
+  0x2194, /*U+2194*/ /**/
+  0x2195, /*U+2195*/ /**/
+  0x2196, /*U+2196*/ /**/
+  0x2197, /*U+2197*/ /**/
+  0x2198, /*U+2198*/ /**/
+  0x2199, /*U+2199*/ /**/
+  0x219A, /*U+219A*/ /**/
+  0x219B, /*U+219B*/ /**/
+  0x219C, /*U+219C*/ /**/
+  0x219D, /*U+219D*/ /**/
+  0x219E, /*U+219E*/ /**/
+  0x219F, /*U+219F*/ /**/
+  0x21A0, /*U+21A0*/ /**/
+  0x21A1, /*U+21A1*/ /**/
+  0x21A2, /*U+21A2*/ /**/
+  0x21A3, /*U+21A3*/ /**/
+  0x21A4, /*U+21A4*/ /**/
+  0x21A5, /*U+21A5*/ /**/
+  0x21A6, /*U+21A6*/ /**/
+  0x21A7, /*U+21A7*/ /**/
+  0x21A8, /*U+21A8*/ /**/
+  0x21A9, /*U+21A9*/ /**/
+  0x21AA, /*U+21AA*/ /**/
+  0x21AB, /*U+21AB*/ /**/
+  0x21AC, /*U+21AC*/ /**/
+  0x21AD, /*U+21AD*/ /**/
+  0x21AE, /*U+21AE*/ /**/
+  0x21AF, /*U+21AF*/ /**/
+  0x21B0, /*U+21B0*/ /**/
+  0x21B1, /*U+21B1*/ /**/
+  0x21B2, /*U+21B2*/ /**/
+  0x21B3, /*U+21B3*/ /**/
+  0x21B4, /*U+21B4*/ /**/
+  0x21B5, /*U+21B5*/ /**/
+  0x21B6, /*U+21B6*/ /**/
+  0x21B7, /*U+21B7*/ /**/
+  0x21B8, /*U+21B8*/ /**/
+  0x21B9, /*U+21B9*/ /**/
+  0x21BA, /*U+21BA*/ /**/
+  0x21BB, /*U+21BB*/ /**/
+  0x21BC, /*U+21BC*/ /**/
+  0x21BD, /*U+21BD*/ /**/
+  0x21BE, /*U+21BE*/ /**/
+  0x21BF, /*U+21BF*/ /**/
+};
+
+static const u_int16_t lower_table_7[128] = {
+  0x2480, /*U+2480*/ /**/
+  0x2481, /*U+2481*/ /**/
+  0x2482, /*U+2482*/ /**/
+  0x2483, /*U+2483*/ /**/
+  0x2484, /*U+2484*/ /**/
+  0x2485, /*U+2485*/ /**/
+  0x2486, /*U+2486*/ /**/
+  0x2487, /*U+2487*/ /**/
+  0x2488, /*U+2488*/ /**/
+  0x2489, /*U+2489*/ /**/
+  0x248A, /*U+248A*/ /**/
+  0x248B, /*U+248B*/ /**/
+  0x248C, /*U+248C*/ /**/
+  0x248D, /*U+248D*/ /**/
+  0x248E, /*U+248E*/ /**/
+  0x248F, /*U+248F*/ /**/
+  0x2490, /*U+2490*/ /**/
+  0x2491, /*U+2491*/ /**/
+  0x2492, /*U+2492*/ /**/
+  0x2493, /*U+2493*/ /**/
+  0x2494, /*U+2494*/ /**/
+  0x2495, /*U+2495*/ /**/
+  0x2496, /*U+2496*/ /**/
+  0x2497, /*U+2497*/ /**/
+  0x2498, /*U+2498*/ /**/
+  0x2499, /*U+2499*/ /**/
+  0x249A, /*U+249A*/ /**/
+  0x249B, /*U+249B*/ /**/
+  0x249C, /*U+249C*/ /**/
+  0x249D, /*U+249D*/ /**/
+  0x249E, /*U+249E*/ /**/
+  0x249F, /*U+249F*/ /**/
+  0x24A0, /*U+24A0*/ /**/
+  0x24A1, /*U+24A1*/ /**/
+  0x24A2, /*U+24A2*/ /**/
+  0x24A3, /*U+24A3*/ /**/
+  0x24A4, /*U+24A4*/ /**/
+  0x24A5, /*U+24A5*/ /**/
+  0x24A6, /*U+24A6*/ /**/
+  0x24A7, /*U+24A7*/ /**/
+  0x24A8, /*U+24A8*/ /**/
+  0x24A9, /*U+24A9*/ /**/
+  0x24AA, /*U+24AA*/ /**/
+  0x24AB, /*U+24AB*/ /**/
+  0x24AC, /*U+24AC*/ /**/
+  0x24AD, /*U+24AD*/ /**/
+  0x24AE, /*U+24AE*/ /**/
+  0x24AF, /*U+24AF*/ /**/
+  0x24B0, /*U+24B0*/ /**/
+  0x24B1, /*U+24B1*/ /**/
+  0x24B2, /*U+24B2*/ /**/
+  0x24B3, /*U+24B3*/ /**/
+  0x24B4, /*U+24B4*/ /**/
+  0x24B5, /*U+24B5*/ /**/
+  0x24D0, /*U+24B6*/ /*CIRCLED LATIN CAPITAL LETTER A*/
+  0x24D1, /*U+24B7*/ /*CIRCLED LATIN CAPITAL LETTER B*/
+  0x24D2, /*U+24B8*/ /*CIRCLED LATIN CAPITAL LETTER C*/
+  0x24D3, /*U+24B9*/ /*CIRCLED LATIN CAPITAL LETTER D*/
+  0x24D4, /*U+24BA*/ /*CIRCLED LATIN CAPITAL LETTER E*/
+  0x24D5, /*U+24BB*/ /*CIRCLED LATIN CAPITAL LETTER F*/
+  0x24D6, /*U+24BC*/ /*CIRCLED LATIN CAPITAL LETTER G*/
+  0x24D7, /*U+24BD*/ /*CIRCLED LATIN CAPITAL LETTER H*/
+  0x24D8, /*U+24BE*/ /*CIRCLED LATIN CAPITAL LETTER I*/
+  0x24D9, /*U+24BF*/ /*CIRCLED LATIN CAPITAL LETTER J*/
+  0x24DA, /*U+24C0*/ /*CIRCLED LATIN CAPITAL LETTER K*/
+  0x24DB, /*U+24C1*/ /*CIRCLED LATIN CAPITAL LETTER L*/
+  0x24DC, /*U+24C2*/ /*CIRCLED LATIN CAPITAL LETTER M*/
+  0x24DD, /*U+24C3*/ /*CIRCLED LATIN CAPITAL LETTER N*/
+  0x24DE, /*U+24C4*/ /*CIRCLED LATIN CAPITAL LETTER O*/
+  0x24DF, /*U+24C5*/ /*CIRCLED LATIN CAPITAL LETTER P*/
+  0x24E0, /*U+24C6*/ /*CIRCLED LATIN CAPITAL LETTER Q*/
+  0x24E1, /*U+24C7*/ /*CIRCLED LATIN CAPITAL LETTER R*/
+  0x24E2, /*U+24C8*/ /*CIRCLED LATIN CAPITAL LETTER S*/
+  0x24E3, /*U+24C9*/ /*CIRCLED LATIN CAPITAL LETTER T*/
+  0x24E4, /*U+24CA*/ /*CIRCLED LATIN CAPITAL LETTER U*/
+  0x24E5, /*U+24CB*/ /*CIRCLED LATIN CAPITAL LETTER V*/
+  0x24E6, /*U+24CC*/ /*CIRCLED LATIN CAPITAL LETTER W*/
+  0x24E7, /*U+24CD*/ /*CIRCLED LATIN CAPITAL LETTER X*/
+  0x24E8, /*U+24CE*/ /*CIRCLED LATIN CAPITAL LETTER Y*/
+  0x24E9, /*U+24CF*/ /*CIRCLED LATIN CAPITAL LETTER Z*/
+  0x24D0, /*U+24D0*/ /**/
+  0x24D1, /*U+24D1*/ /**/
+  0x24D2, /*U+24D2*/ /**/
+  0x24D3, /*U+24D3*/ /**/
+  0x24D4, /*U+24D4*/ /**/
+  0x24D5, /*U+24D5*/ /**/
+  0x24D6, /*U+24D6*/ /**/
+  0x24D7, /*U+24D7*/ /**/
+  0x24D8, /*U+24D8*/ /**/
+  0x24D9, /*U+24D9*/ /**/
+  0x24DA, /*U+24DA*/ /**/
+  0x24DB, /*U+24DB*/ /**/
+  0x24DC, /*U+24DC*/ /**/
+  0x24DD, /*U+24DD*/ /**/
+  0x24DE, /*U+24DE*/ /**/
+  0x24DF, /*U+24DF*/ /**/
+  0x24E0, /*U+24E0*/ /**/
+  0x24E1, /*U+24E1*/ /**/
+  0x24E2, /*U+24E2*/ /**/
+  0x24E3, /*U+24E3*/ /**/
+  0x24E4, /*U+24E4*/ /**/
+  0x24E5, /*U+24E5*/ /**/
+  0x24E6, /*U+24E6*/ /**/
+  0x24E7, /*U+24E7*/ /**/
+  0x24E8, /*U+24E8*/ /**/
+  0x24E9, /*U+24E9*/ /**/
+  0x24EA, /*U+24EA*/ /**/
+  0x24EB, /*U+24EB*/ /**/
+  0x24EC, /*U+24EC*/ /**/
+  0x24ED, /*U+24ED*/ /**/
+  0x24EE, /*U+24EE*/ /**/
+  0x24EF, /*U+24EF*/ /**/
+  0x24F0, /*U+24F0*/ /**/
+  0x24F1, /*U+24F1*/ /**/
+  0x24F2, /*U+24F2*/ /**/
+  0x24F3, /*U+24F3*/ /**/
+  0x24F4, /*U+24F4*/ /**/
+  0x24F5, /*U+24F5*/ /**/
+  0x24F6, /*U+24F6*/ /**/
+  0x24F7, /*U+24F7*/ /**/
+  0x24F8, /*U+24F8*/ /**/
+  0x24F9, /*U+24F9*/ /**/
+  0x24FA, /*U+24FA*/ /**/
+  0x24FB, /*U+24FB*/ /**/
+  0x24FC, /*U+24FC*/ /**/
+  0x24FD, /*U+24FD*/ /**/
+  0x24FE, /*U+24FE*/ /**/
+  0x24FF, /*U+24FF*/ /**/
+};
+
+static const u_int16_t lower_table_8[256] = {
+  0x2C30, /*U+2C00*/ /*GLAGOLITIC CAPITAL LETTER AZU*/
+  0x2C31, /*U+2C01*/ /*GLAGOLITIC CAPITAL LETTER BUKY*/
+  0x2C32, /*U+2C02*/ /*GLAGOLITIC CAPITAL LETTER VEDE*/
+  0x2C33, /*U+2C03*/ /*GLAGOLITIC CAPITAL LETTER GLAGOLI*/
+  0x2C34, /*U+2C04*/ /*GLAGOLITIC CAPITAL LETTER DOBRO*/
+  0x2C35, /*U+2C05*/ /*GLAGOLITIC CAPITAL LETTER YESTU*/
+  0x2C36, /*U+2C06*/ /*GLAGOLITIC CAPITAL LETTER ZHIVETE*/
+  0x2C37, /*U+2C07*/ /*GLAGOLITIC CAPITAL LETTER DZELO*/
+  0x2C38, /*U+2C08*/ /*GLAGOLITIC CAPITAL LETTER ZEMLJA*/
+  0x2C39, /*U+2C09*/ /*GLAGOLITIC CAPITAL LETTER IZHE*/
+  0x2C3A, /*U+2C0A*/ /*GLAGOLITIC CAPITAL LETTER INITIAL IZHE*/
+  0x2C3B, /*U+2C0B*/ /*GLAGOLITIC CAPITAL LETTER I*/
+  0x2C3C, /*U+2C0C*/ /*GLAGOLITIC CAPITAL LETTER DJERVI*/
+  0x2C3D, /*U+2C0D*/ /*GLAGOLITIC CAPITAL LETTER KAKO*/
+  0x2C3E, /*U+2C0E*/ /*GLAGOLITIC CAPITAL LETTER LJUDIJE*/
+  0x2C3F, /*U+2C0F*/ /*GLAGOLITIC CAPITAL LETTER MYSLITE*/
+  0x2C40, /*U+2C10*/ /*GLAGOLITIC CAPITAL LETTER NASHI*/
+  0x2C41, /*U+2C11*/ /*GLAGOLITIC CAPITAL LETTER ONU*/
+  0x2C42, /*U+2C12*/ /*GLAGOLITIC CAPITAL LETTER POKOJI*/
+  0x2C43, /*U+2C13*/ /*GLAGOLITIC CAPITAL LETTER RITSI*/
+  0x2C44, /*U+2C14*/ /*GLAGOLITIC CAPITAL LETTER SLOVO*/
+  0x2C45, /*U+2C15*/ /*GLAGOLITIC CAPITAL LETTER TVRIDO*/
+  0x2C46, /*U+2C16*/ /*GLAGOLITIC CAPITAL LETTER UKU*/
+  0x2C47, /*U+2C17*/ /*GLAGOLITIC CAPITAL LETTER FRITU*/
+  0x2C48, /*U+2C18*/ /*GLAGOLITIC CAPITAL LETTER HERU*/
+  0x2C49, /*U+2C19*/ /*GLAGOLITIC CAPITAL LETTER OTU*/
+  0x2C4A, /*U+2C1A*/ /*GLAGOLITIC CAPITAL LETTER PE*/
+  0x2C4B, /*U+2C1B*/ /*GLAGOLITIC CAPITAL LETTER SHTA*/
+  0x2C4C, /*U+2C1C*/ /*GLAGOLITIC CAPITAL LETTER TSI*/
+  0x2C4D, /*U+2C1D*/ /*GLAGOLITIC CAPITAL LETTER CHRIVI*/
+  0x2C4E, /*U+2C1E*/ /*GLAGOLITIC CAPITAL LETTER SHA*/
+  0x2C4F, /*U+2C1F*/ /*GLAGOLITIC CAPITAL LETTER YERU*/
+  0x2C50, /*U+2C20*/ /*GLAGOLITIC CAPITAL LETTER YERI*/
+  0x2C51, /*U+2C21*/ /*GLAGOLITIC CAPITAL LETTER YATI*/
+  0x2C52, /*U+2C22*/ /*GLAGOLITIC CAPITAL LETTER SPIDERY HA*/
+  0x2C53, /*U+2C23*/ /*GLAGOLITIC CAPITAL LETTER YU*/
+  0x2C54, /*U+2C24*/ /*GLAGOLITIC CAPITAL LETTER SMALL YUS*/
+  0x2C55, /*U+2C25*/ /*GLAGOLITIC CAPITAL LETTER SMALL YUS WITH TAIL*/
+  0x2C56, /*U+2C26*/ /*GLAGOLITIC CAPITAL LETTER YO*/
+  0x2C57, /*U+2C27*/ /*GLAGOLITIC CAPITAL LETTER IOTATED SMALL YUS*/
+  0x2C58, /*U+2C28*/ /*GLAGOLITIC CAPITAL LETTER BIG YUS*/
+  0x2C59, /*U+2C29*/ /*GLAGOLITIC CAPITAL LETTER IOTATED BIG YUS*/
+  0x2C5A, /*U+2C2A*/ /*GLAGOLITIC CAPITAL LETTER FITA*/
+  0x2C5B, /*U+2C2B*/ /*GLAGOLITIC CAPITAL LETTER IZHITSA*/
+  0x2C5C, /*U+2C2C*/ /*GLAGOLITIC CAPITAL LETTER SHTAPIC*/
+  0x2C5D, /*U+2C2D*/ /*GLAGOLITIC CAPITAL LETTER TROKUTASTI A*/
+  0x2C5E, /*U+2C2E*/ /*GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE*/
+  0x2C2F, /*U+2C2F*/ /**/
+  0x2C30, /*U+2C30*/ /**/
+  0x2C31, /*U+2C31*/ /**/
+  0x2C32, /*U+2C32*/ /**/
+  0x2C33, /*U+2C33*/ /**/
+  0x2C34, /*U+2C34*/ /**/
+  0x2C35, /*U+2C35*/ /**/
+  0x2C36, /*U+2C36*/ /**/
+  0x2C37, /*U+2C37*/ /**/
+  0x2C38, /*U+2C38*/ /**/
+  0x2C39, /*U+2C39*/ /**/
+  0x2C3A, /*U+2C3A*/ /**/
+  0x2C3B, /*U+2C3B*/ /**/
+  0x2C3C, /*U+2C3C*/ /**/
+  0x2C3D, /*U+2C3D*/ /**/
+  0x2C3E, /*U+2C3E*/ /**/
+  0x2C3F, /*U+2C3F*/ /**/
+  0x2C40, /*U+2C40*/ /**/
+  0x2C41, /*U+2C41*/ /**/
+  0x2C42, /*U+2C42*/ /**/
+  0x2C43, /*U+2C43*/ /**/
+  0x2C44, /*U+2C44*/ /**/
+  0x2C45, /*U+2C45*/ /**/
+  0x2C46, /*U+2C46*/ /**/
+  0x2C47, /*U+2C47*/ /**/
+  0x2C48, /*U+2C48*/ /**/
+  0x2C49, /*U+2C49*/ /**/
+  0x2C4A, /*U+2C4A*/ /**/
+  0x2C4B, /*U+2C4B*/ /**/
+  0x2C4C, /*U+2C4C*/ /**/
+  0x2C4D, /*U+2C4D*/ /**/
+  0x2C4E, /*U+2C4E*/ /**/
+  0x2C4F, /*U+2C4F*/ /**/
+  0x2C50, /*U+2C50*/ /**/
+  0x2C51, /*U+2C51*/ /**/
+  0x2C52, /*U+2C52*/ /**/
+  0x2C53, /*U+2C53*/ /**/
+  0x2C54, /*U+2C54*/ /**/
+  0x2C55, /*U+2C55*/ /**/
+  0x2C56, /*U+2C56*/ /**/
+  0x2C57, /*U+2C57*/ /**/
+  0x2C58, /*U+2C58*/ /**/
+  0x2C59, /*U+2C59*/ /**/
+  0x2C5A, /*U+2C5A*/ /**/
+  0x2C5B, /*U+2C5B*/ /**/
+  0x2C5C, /*U+2C5C*/ /**/
+  0x2C5D, /*U+2C5D*/ /**/
+  0x2C5E, /*U+2C5E*/ /**/
+  0x2C5F, /*U+2C5F*/ /**/
+  0x2C61, /*U+2C60*/ /*LATIN CAPITAL LETTER L WITH DOUBLE BAR*/
+  0x2C61, /*U+2C61*/ /**/
+  0x026B, /*U+2C62*/ /*LATIN CAPITAL LETTER L WITH MIDDLE TILDE*/
+  0x1D7D, /*U+2C63*/ /*LATIN CAPITAL LETTER P WITH STROKE*/
+  0x027D, /*U+2C64*/ /*LATIN CAPITAL LETTER R WITH TAIL*/
+  0x2C65, /*U+2C65*/ /**/
+  0x2C66, /*U+2C66*/ /**/
+  0x2C68, /*U+2C67*/ /*LATIN CAPITAL LETTER H WITH DESCENDER*/
+  0x2C68, /*U+2C68*/ /**/
+  0x2C6A, /*U+2C69*/ /*LATIN CAPITAL LETTER K WITH DESCENDER*/
+  0x2C6A, /*U+2C6A*/ /**/
+  0x2C6C, /*U+2C6B*/ /*LATIN CAPITAL LETTER Z WITH DESCENDER*/
+  0x2C6C, /*U+2C6C*/ /**/
+  0x0251, /*U+2C6D*/ /*LATIN CAPITAL LETTER ALPHA*/
+  0x0271, /*U+2C6E*/ /*LATIN CAPITAL LETTER M WITH HOOK*/
+  0x0250, /*U+2C6F*/ /*LATIN CAPITAL LETTER TURNED A*/
+  0x0252, /*U+2C70*/ /*LATIN CAPITAL LETTER TURNED ALPHA*/
+  0x2C71, /*U+2C71*/ /**/
+  0x2C73, /*U+2C72*/ /*LATIN CAPITAL LETTER W WITH HOOK*/
+  0x2C73, /*U+2C73*/ /**/
+  0x2C74, /*U+2C74*/ /**/
+  0x2C76, /*U+2C75*/ /*LATIN CAPITAL LETTER HALF H*/
+  0x2C76, /*U+2C76*/ /**/
+  0x2C77, /*U+2C77*/ /**/
+  0x2C78, /*U+2C78*/ /**/
+  0x2C79, /*U+2C79*/ /**/
+  0x2C7A, /*U+2C7A*/ /**/
+  0x2C7B, /*U+2C7B*/ /**/
+  0x2C7C, /*U+2C7C*/ /**/
+  0x2C7D, /*U+2C7D*/ /**/
+  0x023F, /*U+2C7E*/ /*LATIN CAPITAL LETTER S WITH SWASH TAIL*/
+  0x0240, /*U+2C7F*/ /*LATIN CAPITAL LETTER Z WITH SWASH TAIL*/
+  0x2C81, /*U+2C80*/ /*COPTIC CAPITAL LETTER ALFA*/
+  0x2C81, /*U+2C81*/ /**/
+  0x2C83, /*U+2C82*/ /*COPTIC CAPITAL LETTER VIDA*/
+  0x2C83, /*U+2C83*/ /**/
+  0x2C85, /*U+2C84*/ /*COPTIC CAPITAL LETTER GAMMA*/
+  0x2C85, /*U+2C85*/ /**/
+  0x2C87, /*U+2C86*/ /*COPTIC CAPITAL LETTER DALDA*/
+  0x2C87, /*U+2C87*/ /**/
+  0x2C89, /*U+2C88*/ /*COPTIC CAPITAL LETTER EIE*/
+  0x2C89, /*U+2C89*/ /**/
+  0x2C8B, /*U+2C8A*/ /*COPTIC CAPITAL LETTER SOU*/
+  0x2C8B, /*U+2C8B*/ /**/
+  0x2C8D, /*U+2C8C*/ /*COPTIC CAPITAL LETTER ZATA*/
+  0x2C8D, /*U+2C8D*/ /**/
+  0x2C8F, /*U+2C8E*/ /*COPTIC CAPITAL LETTER HATE*/
+  0x2C8F, /*U+2C8F*/ /**/
+  0x2C91, /*U+2C90*/ /*COPTIC CAPITAL LETTER THETHE*/
+  0x2C91, /*U+2C91*/ /**/
+  0x2C93, /*U+2C92*/ /*COPTIC CAPITAL LETTER IAUDA*/
+  0x2C93, /*U+2C93*/ /**/
+  0x2C95, /*U+2C94*/ /*COPTIC CAPITAL LETTER KAPA*/
+  0x2C95, /*U+2C95*/ /**/
+  0x2C97, /*U+2C96*/ /*COPTIC CAPITAL LETTER LAULA*/
+  0x2C97, /*U+2C97*/ /**/
+  0x2C99, /*U+2C98*/ /*COPTIC CAPITAL LETTER MI*/
+  0x2C99, /*U+2C99*/ /**/
+  0x2C9B, /*U+2C9A*/ /*COPTIC CAPITAL LETTER NI*/
+  0x2C9B, /*U+2C9B*/ /**/
+  0x2C9D, /*U+2C9C*/ /*COPTIC CAPITAL LETTER KSI*/
+  0x2C9D, /*U+2C9D*/ /**/
+  0x2C9F, /*U+2C9E*/ /*COPTIC CAPITAL LETTER O*/
+  0x2C9F, /*U+2C9F*/ /**/
+  0x2CA1, /*U+2CA0*/ /*COPTIC CAPITAL LETTER PI*/
+  0x2CA1, /*U+2CA1*/ /**/
+  0x2CA3, /*U+2CA2*/ /*COPTIC CAPITAL LETTER RO*/
+  0x2CA3, /*U+2CA3*/ /**/
+  0x2CA5, /*U+2CA4*/ /*COPTIC CAPITAL LETTER SIMA*/
+  0x2CA5, /*U+2CA5*/ /**/
+  0x2CA7, /*U+2CA6*/ /*COPTIC CAPITAL LETTER TAU*/
+  0x2CA7, /*U+2CA7*/ /**/
+  0x2CA9, /*U+2CA8*/ /*COPTIC CAPITAL LETTER UA*/
+  0x2CA9, /*U+2CA9*/ /**/
+  0x2CAB, /*U+2CAA*/ /*COPTIC CAPITAL LETTER FI*/
+  0x2CAB, /*U+2CAB*/ /**/
+  0x2CAD, /*U+2CAC*/ /*COPTIC CAPITAL LETTER KHI*/
+  0x2CAD, /*U+2CAD*/ /**/
+  0x2CAF, /*U+2CAE*/ /*COPTIC CAPITAL LETTER PSI*/
+  0x2CAF, /*U+2CAF*/ /**/
+  0x2CB1, /*U+2CB0*/ /*COPTIC CAPITAL LETTER OOU*/
+  0x2CB1, /*U+2CB1*/ /**/
+  0x2CB3, /*U+2CB2*/ /*COPTIC CAPITAL LETTER DIALECT-P ALEF*/
+  0x2CB3, /*U+2CB3*/ /**/
+  0x2CB5, /*U+2CB4*/ /*COPTIC CAPITAL LETTER OLD COPTIC AIN*/
+  0x2CB5, /*U+2CB5*/ /**/
+  0x2CB7, /*U+2CB6*/ /*COPTIC CAPITAL LETTER CRYPTOGRAMMIC EIE*/
+  0x2CB7, /*U+2CB7*/ /**/
+  0x2CB9, /*U+2CB8*/ /*COPTIC CAPITAL LETTER DIALECT-P KAPA*/
+  0x2CB9, /*U+2CB9*/ /**/
+  0x2CBB, /*U+2CBA*/ /*COPTIC CAPITAL LETTER DIALECT-P NI*/
+  0x2CBB, /*U+2CBB*/ /**/
+  0x2CBD, /*U+2CBC*/ /*COPTIC CAPITAL LETTER CRYPTOGRAMMIC NI*/
+  0x2CBD, /*U+2CBD*/ /**/
+  0x2CBF, /*U+2CBE*/ /*COPTIC CAPITAL LETTER OLD COPTIC OOU*/
+  0x2CBF, /*U+2CBF*/ /**/
+  0x2CC1, /*U+2CC0*/ /*COPTIC CAPITAL LETTER SAMPI*/
+  0x2CC1, /*U+2CC1*/ /**/
+  0x2CC3, /*U+2CC2*/ /*COPTIC CAPITAL LETTER CROSSED SHEI*/
+  0x2CC3, /*U+2CC3*/ /**/
+  0x2CC5, /*U+2CC4*/ /*COPTIC CAPITAL LETTER OLD COPTIC SHEI*/
+  0x2CC5, /*U+2CC5*/ /**/
+  0x2CC7, /*U+2CC6*/ /*COPTIC CAPITAL LETTER OLD COPTIC ESH*/
+  0x2CC7, /*U+2CC7*/ /**/
+  0x2CC9, /*U+2CC8*/ /*COPTIC CAPITAL LETTER AKHMIMIC KHEI*/
+  0x2CC9, /*U+2CC9*/ /**/
+  0x2CCB, /*U+2CCA*/ /*COPTIC CAPITAL LETTER DIALECT-P HORI*/
+  0x2CCB, /*U+2CCB*/ /**/
+  0x2CCD, /*U+2CCC*/ /*COPTIC CAPITAL LETTER OLD COPTIC HORI*/
+  0x2CCD, /*U+2CCD*/ /**/
+  0x2CCF, /*U+2CCE*/ /*COPTIC CAPITAL LETTER OLD COPTIC HA*/
+  0x2CCF, /*U+2CCF*/ /**/
+  0x2CD1, /*U+2CD0*/ /*COPTIC CAPITAL LETTER L-SHAPED HA*/
+  0x2CD1, /*U+2CD1*/ /**/
+  0x2CD3, /*U+2CD2*/ /*COPTIC CAPITAL LETTER OLD COPTIC HEI*/
+  0x2CD3, /*U+2CD3*/ /**/
+  0x2CD5, /*U+2CD4*/ /*COPTIC CAPITAL LETTER OLD COPTIC HAT*/
+  0x2CD5, /*U+2CD5*/ /**/
+  0x2CD7, /*U+2CD6*/ /*COPTIC CAPITAL LETTER OLD COPTIC GANGIA*/
+  0x2CD7, /*U+2CD7*/ /**/
+  0x2CD9, /*U+2CD8*/ /*COPTIC CAPITAL LETTER OLD COPTIC DJA*/
+  0x2CD9, /*U+2CD9*/ /**/
+  0x2CDB, /*U+2CDA*/ /*COPTIC CAPITAL LETTER OLD COPTIC SHIMA*/
+  0x2CDB, /*U+2CDB*/ /**/
+  0x2CDD, /*U+2CDC*/ /*COPTIC CAPITAL LETTER OLD NUBIAN SHIMA*/
+  0x2CDD, /*U+2CDD*/ /**/
+  0x2CDF, /*U+2CDE*/ /*COPTIC CAPITAL LETTER OLD NUBIAN NGI*/
+  0x2CDF, /*U+2CDF*/ /**/
+  0x2CE1, /*U+2CE0*/ /*COPTIC CAPITAL LETTER OLD NUBIAN NYI*/
+  0x2CE1, /*U+2CE1*/ /**/
+  0x2CE3, /*U+2CE2*/ /*COPTIC CAPITAL LETTER OLD NUBIAN WAU*/
+  0x2CE3, /*U+2CE3*/ /**/
+  0x2CE4, /*U+2CE4*/ /**/
+  0x2CE5, /*U+2CE5*/ /**/
+  0x2CE6, /*U+2CE6*/ /**/
+  0x2CE7, /*U+2CE7*/ /**/
+  0x2CE8, /*U+2CE8*/ /**/
+  0x2CE9, /*U+2CE9*/ /**/
+  0x2CEA, /*U+2CEA*/ /**/
+  0x2CEC, /*U+2CEB*/ /*COPTIC CAPITAL LETTER CRYPTOGRAMMIC SHEI*/
+  0x2CEC, /*U+2CEC*/ /**/
+  0x2CEE, /*U+2CED*/ /*COPTIC CAPITAL LETTER CRYPTOGRAMMIC GANGIA*/
+  0x2CEE, /*U+2CEE*/ /**/
+  0x2CEF, /*U+2CEF*/ /**/
+  0x2CF0, /*U+2CF0*/ /**/
+  0x2CF1, /*U+2CF1*/ /**/
+  0x2CF2, /*U+2CF2*/ /**/
+  0x2CF3, /*U+2CF3*/ /**/
+  0x2CF4, /*U+2CF4*/ /**/
+  0x2CF5, /*U+2CF5*/ /**/
+  0x2CF6, /*U+2CF6*/ /**/
+  0x2CF7, /*U+2CF7*/ /**/
+  0x2CF8, /*U+2CF8*/ /**/
+  0x2CF9, /*U+2CF9*/ /**/
+  0x2CFA, /*U+2CFA*/ /**/
+  0x2CFB, /*U+2CFB*/ /**/
+  0x2CFC, /*U+2CFC*/ /**/
+  0x2CFD, /*U+2CFD*/ /**/
+  0x2CFE, /*U+2CFE*/ /**/
+  0x2CFF, /*U+2CFF*/ /**/
+};
+
+static const u_int16_t lower_table_9[128] = {
+  0xA641, /*U+A640*/ /*CYRILLIC CAPITAL LETTER ZEMLYA*/
+  0xA641, /*U+A641*/ /**/
+  0xA643, /*U+A642*/ /*CYRILLIC CAPITAL LETTER DZELO*/
+  0xA643, /*U+A643*/ /**/
+  0xA645, /*U+A644*/ /*CYRILLIC CAPITAL LETTER REVERSED DZE*/
+  0xA645, /*U+A645*/ /**/
+  0xA647, /*U+A646*/ /*CYRILLIC CAPITAL LETTER IOTA*/
+  0xA647, /*U+A647*/ /**/
+  0xA649, /*U+A648*/ /*CYRILLIC CAPITAL LETTER DJERV*/
+  0xA649, /*U+A649*/ /**/
+  0xA64B, /*U+A64A*/ /*CYRILLIC CAPITAL LETTER MONOGRAPH UK*/
+  0xA64B, /*U+A64B*/ /**/
+  0xA64D, /*U+A64C*/ /*CYRILLIC CAPITAL LETTER BROAD OMEGA*/
+  0xA64D, /*U+A64D*/ /**/
+  0xA64F, /*U+A64E*/ /*CYRILLIC CAPITAL LETTER NEUTRAL YER*/
+  0xA64F, /*U+A64F*/ /**/
+  0xA651, /*U+A650*/ /*CYRILLIC CAPITAL LETTER YERU WITH BACK YER*/
+  0xA651, /*U+A651*/ /**/
+  0xA653, /*U+A652*/ /*CYRILLIC CAPITAL LETTER IOTIFIED YAT*/
+  0xA653, /*U+A653*/ /**/
+  0xA655, /*U+A654*/ /*CYRILLIC CAPITAL LETTER REVERSED YU*/
+  0xA655, /*U+A655*/ /**/
+  0xA657, /*U+A656*/ /*CYRILLIC CAPITAL LETTER IOTIFIED A*/
+  0xA657, /*U+A657*/ /**/
+  0xA659, /*U+A658*/ /*CYRILLIC CAPITAL LETTER CLOSED LITTLE YUS*/
+  0xA659, /*U+A659*/ /**/
+  0xA65B, /*U+A65A*/ /*CYRILLIC CAPITAL LETTER BLENDED YUS*/
+  0xA65B, /*U+A65B*/ /**/
+  0xA65D, /*U+A65C*/ /*CYRILLIC CAPITAL LETTER IOTIFIED CLOSED LITTLE YUS*/
+  0xA65D, /*U+A65D*/ /**/
+  0xA65F, /*U+A65E*/ /*CYRILLIC CAPITAL LETTER YN*/
+  0xA65F, /*U+A65F*/ /**/
+  0xA661, /*U+A660*/ /*CYRILLIC CAPITAL LETTER REVERSED TSE*/
+  0xA661, /*U+A661*/ /**/
+  0xA663, /*U+A662*/ /*CYRILLIC CAPITAL LETTER SOFT DE*/
+  0xA663, /*U+A663*/ /**/
+  0xA665, /*U+A664*/ /*CYRILLIC CAPITAL LETTER SOFT EL*/
+  0xA665, /*U+A665*/ /**/
+  0xA667, /*U+A666*/ /*CYRILLIC CAPITAL LETTER SOFT EM*/
+  0xA667, /*U+A667*/ /**/
+  0xA669, /*U+A668*/ /*CYRILLIC CAPITAL LETTER MONOCULAR O*/
+  0xA669, /*U+A669*/ /**/
+  0xA66B, /*U+A66A*/ /*CYRILLIC CAPITAL LETTER BINOCULAR O*/
+  0xA66B, /*U+A66B*/ /**/
+  0xA66D, /*U+A66C*/ /*CYRILLIC CAPITAL LETTER DOUBLE MONOCULAR O*/
+  0xA66D, /*U+A66D*/ /**/
+  0xA66E, /*U+A66E*/ /**/
+  0xA66F, /*U+A66F*/ /**/
+  0xA670, /*U+A670*/ /**/
+  0xA671, /*U+A671*/ /**/
+  0xA672, /*U+A672*/ /**/
+  0xA673, /*U+A673*/ /**/
+  0xA674, /*U+A674*/ /**/
+  0xA675, /*U+A675*/ /**/
+  0xA676, /*U+A676*/ /**/
+  0xA677, /*U+A677*/ /**/
+  0xA678, /*U+A678*/ /**/
+  0xA679, /*U+A679*/ /**/
+  0xA67A, /*U+A67A*/ /**/
+  0xA67B, /*U+A67B*/ /**/
+  0xA67C, /*U+A67C*/ /**/
+  0xA67D, /*U+A67D*/ /**/
+  0xA67E, /*U+A67E*/ /**/
+  0xA67F, /*U+A67F*/ /**/
+  0xA681, /*U+A680*/ /*CYRILLIC CAPITAL LETTER DWE*/
+  0xA681, /*U+A681*/ /**/
+  0xA683, /*U+A682*/ /*CYRILLIC CAPITAL LETTER DZWE*/
+  0xA683, /*U+A683*/ /**/
+  0xA685, /*U+A684*/ /*CYRILLIC CAPITAL LETTER ZHWE*/
+  0xA685, /*U+A685*/ /**/
+  0xA687, /*U+A686*/ /*CYRILLIC CAPITAL LETTER CCHE*/
+  0xA687, /*U+A687*/ /**/
+  0xA689, /*U+A688*/ /*CYRILLIC CAPITAL LETTER DZZE*/
+  0xA689, /*U+A689*/ /**/
+  0xA68B, /*U+A68A*/ /*CYRILLIC CAPITAL LETTER TE WITH MIDDLE HOOK*/
+  0xA68B, /*U+A68B*/ /**/
+  0xA68D, /*U+A68C*/ /*CYRILLIC CAPITAL LETTER TWE*/
+  0xA68D, /*U+A68D*/ /**/
+  0xA68F, /*U+A68E*/ /*CYRILLIC CAPITAL LETTER TSWE*/
+  0xA68F, /*U+A68F*/ /**/
+  0xA691, /*U+A690*/ /*CYRILLIC CAPITAL LETTER TSSE*/
+  0xA691, /*U+A691*/ /**/
+  0xA693, /*U+A692*/ /*CYRILLIC CAPITAL LETTER TCHE*/
+  0xA693, /*U+A693*/ /**/
+  0xA695, /*U+A694*/ /*CYRILLIC CAPITAL LETTER HWE*/
+  0xA695, /*U+A695*/ /**/
+  0xA697, /*U+A696*/ /*CYRILLIC CAPITAL LETTER SHWE*/
+  0xA697, /*U+A697*/ /**/
+  0xA698, /*U+A698*/ /**/
+  0xA699, /*U+A699*/ /**/
+  0xA69A, /*U+A69A*/ /**/
+  0xA69B, /*U+A69B*/ /**/
+  0xA69C, /*U+A69C*/ /**/
+  0xA69D, /*U+A69D*/ /**/
+  0xA69E, /*U+A69E*/ /**/
+  0xA69F, /*U+A69F*/ /**/
+  0xA6A0, /*U+A6A0*/ /**/
+  0xA6A1, /*U+A6A1*/ /**/
+  0xA6A2, /*U+A6A2*/ /**/
+  0xA6A3, /*U+A6A3*/ /**/
+  0xA6A4, /*U+A6A4*/ /**/
+  0xA6A5, /*U+A6A5*/ /**/
+  0xA6A6, /*U+A6A6*/ /**/
+  0xA6A7, /*U+A6A7*/ /**/
+  0xA6A8, /*U+A6A8*/ /**/
+  0xA6A9, /*U+A6A9*/ /**/
+  0xA6AA, /*U+A6AA*/ /**/
+  0xA6AB, /*U+A6AB*/ /**/
+  0xA6AC, /*U+A6AC*/ /**/
+  0xA6AD, /*U+A6AD*/ /**/
+  0xA6AE, /*U+A6AE*/ /**/
+  0xA6AF, /*U+A6AF*/ /**/
+  0xA6B0, /*U+A6B0*/ /**/
+  0xA6B1, /*U+A6B1*/ /**/
+  0xA6B2, /*U+A6B2*/ /**/
+  0xA6B3, /*U+A6B3*/ /**/
+  0xA6B4, /*U+A6B4*/ /**/
+  0xA6B5, /*U+A6B5*/ /**/
+  0xA6B6, /*U+A6B6*/ /**/
+  0xA6B7, /*U+A6B7*/ /**/
+  0xA6B8, /*U+A6B8*/ /**/
+  0xA6B9, /*U+A6B9*/ /**/
+  0xA6BA, /*U+A6BA*/ /**/
+  0xA6BB, /*U+A6BB*/ /**/
+  0xA6BC, /*U+A6BC*/ /**/
+  0xA6BD, /*U+A6BD*/ /**/
+  0xA6BE, /*U+A6BE*/ /**/
+  0xA6BF, /*U+A6BF*/ /**/
+};
+
+static const u_int16_t lower_table_10[192] = {
+  0xA700, /*U+A700*/ /**/
+  0xA701, /*U+A701*/ /**/
+  0xA702, /*U+A702*/ /**/
+  0xA703, /*U+A703*/ /**/
+  0xA704, /*U+A704*/ /**/
+  0xA705, /*U+A705*/ /**/
+  0xA706, /*U+A706*/ /**/
+  0xA707, /*U+A707*/ /**/
+  0xA708, /*U+A708*/ /**/
+  0xA709, /*U+A709*/ /**/
+  0xA70A, /*U+A70A*/ /**/
+  0xA70B, /*U+A70B*/ /**/
+  0xA70C, /*U+A70C*/ /**/
+  0xA70D, /*U+A70D*/ /**/
+  0xA70E, /*U+A70E*/ /**/
+  0xA70F, /*U+A70F*/ /**/
+  0xA710, /*U+A710*/ /**/
+  0xA711, /*U+A711*/ /**/
+  0xA712, /*U+A712*/ /**/
+  0xA713, /*U+A713*/ /**/
+  0xA714, /*U+A714*/ /**/
+  0xA715, /*U+A715*/ /**/
+  0xA716, /*U+A716*/ /**/
+  0xA717, /*U+A717*/ /**/
+  0xA718, /*U+A718*/ /**/
+  0xA719, /*U+A719*/ /**/
+  0xA71A, /*U+A71A*/ /**/
+  0xA71B, /*U+A71B*/ /**/
+  0xA71C, /*U+A71C*/ /**/
+  0xA71D, /*U+A71D*/ /**/
+  0xA71E, /*U+A71E*/ /**/
+  0xA71F, /*U+A71F*/ /**/
+  0xA720, /*U+A720*/ /**/
+  0xA721, /*U+A721*/ /**/
+  0xA723, /*U+A722*/ /*LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF*/
+  0xA723, /*U+A723*/ /**/
+  0xA725, /*U+A724*/ /*LATIN CAPITAL LETTER EGYPTOLOGICAL AIN*/
+  0xA725, /*U+A725*/ /**/
+  0xA727, /*U+A726*/ /*LATIN CAPITAL LETTER HENG*/
+  0xA727, /*U+A727*/ /**/
+  0xA729, /*U+A728*/ /*LATIN CAPITAL LETTER TZ*/
+  0xA729, /*U+A729*/ /**/
+  0xA72B, /*U+A72A*/ /*LATIN CAPITAL LETTER TRESILLO*/
+  0xA72B, /*U+A72B*/ /**/
+  0xA72D, /*U+A72C*/ /*LATIN CAPITAL LETTER CUATRILLO*/
+  0xA72D, /*U+A72D*/ /**/
+  0xA72F, /*U+A72E*/ /*LATIN CAPITAL LETTER CUATRILLO WITH COMMA*/
+  0xA72F, /*U+A72F*/ /**/
+  0xA730, /*U+A730*/ /**/
+  0xA731, /*U+A731*/ /**/
+  0xA733, /*U+A732*/ /*LATIN CAPITAL LETTER AA*/
+  0xA733, /*U+A733*/ /**/
+  0xA735, /*U+A734*/ /*LATIN CAPITAL LETTER AO*/
+  0xA735, /*U+A735*/ /**/
+  0xA737, /*U+A736*/ /*LATIN CAPITAL LETTER AU*/
+  0xA737, /*U+A737*/ /**/
+  0xA739, /*U+A738*/ /*LATIN CAPITAL LETTER AV*/
+  0xA739, /*U+A739*/ /**/
+  0xA73B, /*U+A73A*/ /*LATIN CAPITAL LETTER AV WITH HORIZONTAL BAR*/
+  0xA73B, /*U+A73B*/ /**/
+  0xA73D, /*U+A73C*/ /*LATIN CAPITAL LETTER AY*/
+  0xA73D, /*U+A73D*/ /**/
+  0xA73F, /*U+A73E*/ /*LATIN CAPITAL LETTER REVERSED C WITH DOT*/
+  0xA73F, /*U+A73F*/ /**/
+  0xA741, /*U+A740*/ /*LATIN CAPITAL LETTER K WITH STROKE*/
+  0xA741, /*U+A741*/ /**/
+  0xA743, /*U+A742*/ /*LATIN CAPITAL LETTER K WITH DIAGONAL STROKE*/
+  0xA743, /*U+A743*/ /**/
+  0xA745, /*U+A744*/ /*LATIN CAPITAL LETTER K WITH STROKE AND DIAGONAL STROKE*/
+  0xA745, /*U+A745*/ /**/
+  0xA747, /*U+A746*/ /*LATIN CAPITAL LETTER BROKEN L*/
+  0xA747, /*U+A747*/ /**/
+  0xA749, /*U+A748*/ /*LATIN CAPITAL LETTER L WITH HIGH STROKE*/
+  0xA749, /*U+A749*/ /**/
+  0xA74B, /*U+A74A*/ /*LATIN CAPITAL LETTER O WITH LONG STROKE OVERLAY*/
+  0xA74B, /*U+A74B*/ /**/
+  0xA74D, /*U+A74C*/ /*LATIN CAPITAL LETTER O WITH LOOP*/
+  0xA74D, /*U+A74D*/ /**/
+  0xA74F, /*U+A74E*/ /*LATIN CAPITAL LETTER OO*/
+  0xA74F, /*U+A74F*/ /**/
+  0xA751, /*U+A750*/ /*LATIN CAPITAL LETTER P WITH STROKE THROUGH DESCENDER*/
+  0xA751, /*U+A751*/ /**/
+  0xA753, /*U+A752*/ /*LATIN CAPITAL LETTER P WITH FLOURISH*/
+  0xA753, /*U+A753*/ /**/
+  0xA755, /*U+A754*/ /*LATIN CAPITAL LETTER P WITH SQUIRREL TAIL*/
+  0xA755, /*U+A755*/ /**/
+  0xA757, /*U+A756*/ /*LATIN CAPITAL LETTER Q WITH STROKE THROUGH DESCENDER*/
+  0xA757, /*U+A757*/ /**/
+  0xA759, /*U+A758*/ /*LATIN CAPITAL LETTER Q WITH DIAGONAL STROKE*/
+  0xA759, /*U+A759*/ /**/
+  0xA75B, /*U+A75A*/ /*LATIN CAPITAL LETTER R ROTUNDA*/
+  0xA75B, /*U+A75B*/ /**/
+  0xA75D, /*U+A75C*/ /*LATIN CAPITAL LETTER RUM ROTUNDA*/
+  0xA75D, /*U+A75D*/ /**/
+  0xA75F, /*U+A75E*/ /*LATIN CAPITAL LETTER V WITH DIAGONAL STROKE*/
+  0xA75F, /*U+A75F*/ /**/
+  0xA761, /*U+A760*/ /*LATIN CAPITAL LETTER VY*/
+  0xA761, /*U+A761*/ /**/
+  0xA763, /*U+A762*/ /*LATIN CAPITAL LETTER VISIGOTHIC Z*/
+  0xA763, /*U+A763*/ /**/
+  0xA765, /*U+A764*/ /*LATIN CAPITAL LETTER THORN WITH STROKE*/
+  0xA765, /*U+A765*/ /**/
+  0xA767, /*U+A766*/ /*LATIN CAPITAL LETTER THORN WITH STROKE THROUGH DESCENDER*/
+  0xA767, /*U+A767*/ /**/
+  0xA769, /*U+A768*/ /*LATIN CAPITAL LETTER VEND*/
+  0xA769, /*U+A769*/ /**/
+  0xA76B, /*U+A76A*/ /*LATIN CAPITAL LETTER ET*/
+  0xA76B, /*U+A76B*/ /**/
+  0xA76D, /*U+A76C*/ /*LATIN CAPITAL LETTER IS*/
+  0xA76D, /*U+A76D*/ /**/
+  0xA76F, /*U+A76E*/ /*LATIN CAPITAL LETTER CON*/
+  0xA76F, /*U+A76F*/ /**/
+  0xA770, /*U+A770*/ /**/
+  0xA771, /*U+A771*/ /**/
+  0xA772, /*U+A772*/ /**/
+  0xA773, /*U+A773*/ /**/
+  0xA774, /*U+A774*/ /**/
+  0xA775, /*U+A775*/ /**/
+  0xA776, /*U+A776*/ /**/
+  0xA777, /*U+A777*/ /**/
+  0xA778, /*U+A778*/ /**/
+  0xA77A, /*U+A779*/ /*LATIN CAPITAL LETTER INSULAR D*/
+  0xA77A, /*U+A77A*/ /**/
+  0xA77C, /*U+A77B*/ /*LATIN CAPITAL LETTER INSULAR F*/
+  0xA77C, /*U+A77C*/ /**/
+  0x1D79, /*U+A77D*/ /*LATIN CAPITAL LETTER INSULAR G*/
+  0xA77F, /*U+A77E*/ /*LATIN CAPITAL LETTER TURNED INSULAR G*/
+  0xA77F, /*U+A77F*/ /**/
+  0xA781, /*U+A780*/ /*LATIN CAPITAL LETTER TURNED L*/
+  0xA781, /*U+A781*/ /**/
+  0xA783, /*U+A782*/ /*LATIN CAPITAL LETTER INSULAR R*/
+  0xA783, /*U+A783*/ /**/
+  0xA785, /*U+A784*/ /*LATIN CAPITAL LETTER INSULAR S*/
+  0xA785, /*U+A785*/ /**/
+  0xA787, /*U+A786*/ /*LATIN CAPITAL LETTER INSULAR T*/
+  0xA787, /*U+A787*/ /**/
+  0xA788, /*U+A788*/ /**/
+  0xA789, /*U+A789*/ /**/
+  0xA78A, /*U+A78A*/ /**/
+  0xA78C, /*U+A78B*/ /*LATIN CAPITAL LETTER SALTILLO*/
+  0xA78C, /*U+A78C*/ /**/
+  0x0265, /*U+A78D*/ /*LATIN CAPITAL LETTER TURNED H*/
+  0xA78E, /*U+A78E*/ /**/
+  0xA78F, /*U+A78F*/ /**/
+  0xA791, /*U+A790*/ /*LATIN CAPITAL LETTER N WITH DESCENDER*/
+  0xA791, /*U+A791*/ /**/
+  0xA792, /*U+A792*/ /**/
+  0xA793, /*U+A793*/ /**/
+  0xA794, /*U+A794*/ /**/
+  0xA795, /*U+A795*/ /**/
+  0xA796, /*U+A796*/ /**/
+  0xA797, /*U+A797*/ /**/
+  0xA798, /*U+A798*/ /**/
+  0xA799, /*U+A799*/ /**/
+  0xA79A, /*U+A79A*/ /**/
+  0xA79B, /*U+A79B*/ /**/
+  0xA79C, /*U+A79C*/ /**/
+  0xA79D, /*U+A79D*/ /**/
+  0xA79E, /*U+A79E*/ /**/
+  0xA79F, /*U+A79F*/ /**/
+  0xA7A1, /*U+A7A0*/ /*LATIN CAPITAL LETTER G WITH OBLIQUE STROKE*/
+  0xA7A1, /*U+A7A1*/ /**/
+  0xA7A3, /*U+A7A2*/ /*LATIN CAPITAL LETTER K WITH OBLIQUE STROKE*/
+  0xA7A3, /*U+A7A3*/ /**/
+  0xA7A5, /*U+A7A4*/ /*LATIN CAPITAL LETTER N WITH OBLIQUE STROKE*/
+  0xA7A5, /*U+A7A5*/ /**/
+  0xA7A7, /*U+A7A6*/ /*LATIN CAPITAL LETTER R WITH OBLIQUE STROKE*/
+  0xA7A7, /*U+A7A7*/ /**/
+  0xA7A9, /*U+A7A8*/ /*LATIN CAPITAL LETTER S WITH OBLIQUE STROKE*/
+  0xA7A9, /*U+A7A9*/ /**/
+  0xA7AA, /*U+A7AA*/ /**/
+  0xA7AB, /*U+A7AB*/ /**/
+  0xA7AC, /*U+A7AC*/ /**/
+  0xA7AD, /*U+A7AD*/ /**/
+  0xA7AE, /*U+A7AE*/ /**/
+  0xA7AF, /*U+A7AF*/ /**/
+  0xA7B0, /*U+A7B0*/ /**/
+  0xA7B1, /*U+A7B1*/ /**/
+  0xA7B2, /*U+A7B2*/ /**/
+  0xA7B3, /*U+A7B3*/ /**/
+  0xA7B4, /*U+A7B4*/ /**/
+  0xA7B5, /*U+A7B5*/ /**/
+  0xA7B6, /*U+A7B6*/ /**/
+  0xA7B7, /*U+A7B7*/ /**/
+  0xA7B8, /*U+A7B8*/ /**/
+  0xA7B9, /*U+A7B9*/ /**/
+  0xA7BA, /*U+A7BA*/ /**/
+  0xA7BB, /*U+A7BB*/ /**/
+  0xA7BC, /*U+A7BC*/ /**/
+  0xA7BD, /*U+A7BD*/ /**/
+  0xA7BE, /*U+A7BE*/ /**/
+  0xA7BF, /*U+A7BF*/ /**/
+};
+
+static const u_int16_t lower_table_11[64] = {
+  0xFF00, /*U+FF00*/ /**/
+  0xFF01, /*U+FF01*/ /**/
+  0xFF02, /*U+FF02*/ /**/
+  0xFF03, /*U+FF03*/ /**/
+  0xFF04, /*U+FF04*/ /**/
+  0xFF05, /*U+FF05*/ /**/
+  0xFF06, /*U+FF06*/ /**/
+  0xFF07, /*U+FF07*/ /**/
+  0xFF08, /*U+FF08*/ /**/
+  0xFF09, /*U+FF09*/ /**/
+  0xFF0A, /*U+FF0A*/ /**/
+  0xFF0B, /*U+FF0B*/ /**/
+  0xFF0C, /*U+FF0C*/ /**/
+  0xFF0D, /*U+FF0D*/ /**/
+  0xFF0E, /*U+FF0E*/ /**/
+  0xFF0F, /*U+FF0F*/ /**/
+  0xFF10, /*U+FF10*/ /**/
+  0xFF11, /*U+FF11*/ /**/
+  0xFF12, /*U+FF12*/ /**/
+  0xFF13, /*U+FF13*/ /**/
+  0xFF14, /*U+FF14*/ /**/
+  0xFF15, /*U+FF15*/ /**/
+  0xFF16, /*U+FF16*/ /**/
+  0xFF17, /*U+FF17*/ /**/
+  0xFF18, /*U+FF18*/ /**/
+  0xFF19, /*U+FF19*/ /**/
+  0xFF1A, /*U+FF1A*/ /**/
+  0xFF1B, /*U+FF1B*/ /**/
+  0xFF1C, /*U+FF1C*/ /**/
+  0xFF1D, /*U+FF1D*/ /**/
+  0xFF1E, /*U+FF1E*/ /**/
+  0xFF1F, /*U+FF1F*/ /**/
+  0xFF20, /*U+FF20*/ /**/
+  0xFF41, /*U+FF21*/ /*FULLWIDTH LATIN CAPITAL LETTER A*/
+  0xFF42, /*U+FF22*/ /*FULLWIDTH LATIN CAPITAL LETTER B*/
+  0xFF43, /*U+FF23*/ /*FULLWIDTH LATIN CAPITAL LETTER C*/
+  0xFF44, /*U+FF24*/ /*FULLWIDTH LATIN CAPITAL LETTER D*/
+  0xFF45, /*U+FF25*/ /*FULLWIDTH LATIN CAPITAL LETTER E*/
+  0xFF46, /*U+FF26*/ /*FULLWIDTH LATIN CAPITAL LETTER F*/
+  0xFF47, /*U+FF27*/ /*FULLWIDTH LATIN CAPITAL LETTER G*/
+  0xFF48, /*U+FF28*/ /*FULLWIDTH LATIN CAPITAL LETTER H*/
+  0xFF49, /*U+FF29*/ /*FULLWIDTH LATIN CAPITAL LETTER I*/
+  0xFF4A, /*U+FF2A*/ /*FULLWIDTH LATIN CAPITAL LETTER J*/
+  0xFF4B, /*U+FF2B*/ /*FULLWIDTH LATIN CAPITAL LETTER K*/
+  0xFF4C, /*U+FF2C*/ /*FULLWIDTH LATIN CAPITAL LETTER L*/
+  0xFF4D, /*U+FF2D*/ /*FULLWIDTH LATIN CAPITAL LETTER M*/
+  0xFF4E, /*U+FF2E*/ /*FULLWIDTH LATIN CAPITAL LETTER N*/
+  0xFF4F, /*U+FF2F*/ /*FULLWIDTH LATIN CAPITAL LETTER O*/
+  0xFF50, /*U+FF30*/ /*FULLWIDTH LATIN CAPITAL LETTER P*/
+  0xFF51, /*U+FF31*/ /*FULLWIDTH LATIN CAPITAL LETTER Q*/
+  0xFF52, /*U+FF32*/ /*FULLWIDTH LATIN CAPITAL LETTER R*/
+  0xFF53, /*U+FF33*/ /*FULLWIDTH LATIN CAPITAL LETTER S*/
+  0xFF54, /*U+FF34*/ /*FULLWIDTH LATIN CAPITAL LETTER T*/
+  0xFF55, /*U+FF35*/ /*FULLWIDTH LATIN CAPITAL LETTER U*/
+  0xFF56, /*U+FF36*/ /*FULLWIDTH LATIN CAPITAL LETTER V*/
+  0xFF57, /*U+FF37*/ /*FULLWIDTH LATIN CAPITAL LETTER W*/
+  0xFF58, /*U+FF38*/ /*FULLWIDTH LATIN CAPITAL LETTER X*/
+  0xFF59, /*U+FF39*/ /*FULLWIDTH LATIN CAPITAL LETTER Y*/
+  0xFF5A, /*U+FF3A*/ /*FULLWIDTH LATIN CAPITAL LETTER Z*/
+  0xFF3B, /*U+FF3B*/ /**/
+  0xFF3C, /*U+FF3C*/ /**/
+  0xFF3D, /*U+FF3D*/ /**/
+  0xFF3E, /*U+FF3E*/ /**/
+  0xFF3F, /*U+FF3F*/ /**/
+};
+
+static const u_int32_t lower_table_sp_1[64] = {
+  0xD801DC28, /*0xD801DC00*/ /*U+010428*/ /*U+010400*/ /*DESERET CAPITAL LETTER LONG I*/
+  0xD801DC29, /*0xD801DC01*/ /*U+010429*/ /*U+010401*/ /*DESERET CAPITAL LETTER LONG E*/
+  0xD801DC2A, /*0xD801DC02*/ /*U+01042A*/ /*U+010402*/ /*DESERET CAPITAL LETTER LONG A*/
+  0xD801DC2B, /*0xD801DC03*/ /*U+01042B*/ /*U+010403*/ /*DESERET CAPITAL LETTER LONG AH*/
+  0xD801DC2C, /*0xD801DC04*/ /*U+01042C*/ /*U+010404*/ /*DESERET CAPITAL LETTER LONG O*/
+  0xD801DC2D, /*0xD801DC05*/ /*U+01042D*/ /*U+010405*/ /*DESERET CAPITAL LETTER LONG OO*/
+  0xD801DC2E, /*0xD801DC06*/ /*U+01042E*/ /*U+010406*/ /*DESERET CAPITAL LETTER SHORT I*/
+  0xD801DC2F, /*0xD801DC07*/ /*U+01042F*/ /*U+010407*/ /*DESERET CAPITAL LETTER SHORT E*/
+  0xD801DC30, /*0xD801DC08*/ /*U+010430*/ /*U+010408*/ /*DESERET CAPITAL LETTER SHORT A*/
+  0xD801DC31, /*0xD801DC09*/ /*U+010431*/ /*U+010409*/ /*DESERET CAPITAL LETTER SHORT AH*/
+  0xD801DC32, /*0xD801DC0A*/ /*U+010432*/ /*U+01040A*/ /*DESERET CAPITAL LETTER SHORT O*/
+  0xD801DC33, /*0xD801DC0B*/ /*U+010433*/ /*U+01040B*/ /*DESERET CAPITAL LETTER SHORT OO*/
+  0xD801DC34, /*0xD801DC0C*/ /*U+010434*/ /*U+01040C*/ /*DESERET CAPITAL LETTER AY*/
+  0xD801DC35, /*0xD801DC0D*/ /*U+010435*/ /*U+01040D*/ /*DESERET CAPITAL LETTER OW*/
+  0xD801DC36, /*0xD801DC0E*/ /*U+010436*/ /*U+01040E*/ /*DESERET CAPITAL LETTER WU*/
+  0xD801DC37, /*0xD801DC0F*/ /*U+010437*/ /*U+01040F*/ /*DESERET CAPITAL LETTER YEE*/
+  0xD801DC38, /*0xD801DC10*/ /*U+010438*/ /*U+010410*/ /*DESERET CAPITAL LETTER H*/
+  0xD801DC39, /*0xD801DC11*/ /*U+010439*/ /*U+010411*/ /*DESERET CAPITAL LETTER PEE*/
+  0xD801DC3A, /*0xD801DC12*/ /*U+01043A*/ /*U+010412*/ /*DESERET CAPITAL LETTER BEE*/
+  0xD801DC3B, /*0xD801DC13*/ /*U+01043B*/ /*U+010413*/ /*DESERET CAPITAL LETTER TEE*/
+  0xD801DC3C, /*0xD801DC14*/ /*U+01043C*/ /*U+010414*/ /*DESERET CAPITAL LETTER DEE*/
+  0xD801DC3D, /*0xD801DC15*/ /*U+01043D*/ /*U+010415*/ /*DESERET CAPITAL LETTER CHEE*/
+  0xD801DC3E, /*0xD801DC16*/ /*U+01043E*/ /*U+010416*/ /*DESERET CAPITAL LETTER JEE*/
+  0xD801DC3F, /*0xD801DC17*/ /*U+01043F*/ /*U+010417*/ /*DESERET CAPITAL LETTER KAY*/
+  0xD801DC40, /*0xD801DC18*/ /*U+010440*/ /*U+010418*/ /*DESERET CAPITAL LETTER GAY*/
+  0xD801DC41, /*0xD801DC19*/ /*U+010441*/ /*U+010419*/ /*DESERET CAPITAL LETTER EF*/
+  0xD801DC42, /*0xD801DC1A*/ /*U+010442*/ /*U+01041A*/ /*DESERET CAPITAL LETTER VEE*/
+  0xD801DC43, /*0xD801DC1B*/ /*U+010443*/ /*U+01041B*/ /*DESERET CAPITAL LETTER ETH*/
+  0xD801DC44, /*0xD801DC1C*/ /*U+010444*/ /*U+01041C*/ /*DESERET CAPITAL LETTER THEE*/
+  0xD801DC45, /*0xD801DC1D*/ /*U+010445*/ /*U+01041D*/ /*DESERET CAPITAL LETTER ES*/
+  0xD801DC46, /*0xD801DC1E*/ /*U+010446*/ /*U+01041E*/ /*DESERET CAPITAL LETTER ZEE*/
+  0xD801DC47, /*0xD801DC1F*/ /*U+010447*/ /*U+01041F*/ /*DESERET CAPITAL LETTER ESH*/
+  0xD801DC48, /*0xD801DC20*/ /*U+010448*/ /*U+010420*/ /*DESERET CAPITAL LETTER ZHEE*/
+  0xD801DC49, /*0xD801DC21*/ /*U+010449*/ /*U+010421*/ /*DESERET CAPITAL LETTER ER*/
+  0xD801DC4A, /*0xD801DC22*/ /*U+01044A*/ /*U+010422*/ /*DESERET CAPITAL LETTER EL*/
+  0xD801DC4B, /*0xD801DC23*/ /*U+01044B*/ /*U+010423*/ /*DESERET CAPITAL LETTER EM*/
+  0xD801DC4C, /*0xD801DC24*/ /*U+01044C*/ /*U+010424*/ /*DESERET CAPITAL LETTER EN*/
+  0xD801DC4D, /*0xD801DC25*/ /*U+01044D*/ /*U+010425*/ /*DESERET CAPITAL LETTER ENG*/
+  0xD801DC4E, /*0xD801DC26*/ /*U+01044E*/ /*U+010426*/ /*DESERET CAPITAL LETTER OI*/
+  0xD801DC4F, /*0xD801DC27*/ /*U+01044F*/ /*U+010427*/ /*DESERET CAPITAL LETTER EW*/
+  0xD801DC28, /*0xD801DC28*/ /*U+010428*/ /*U+010428*/ /**/
+  0xD801DC29, /*0xD801DC29*/ /*U+010429*/ /*U+010429*/ /**/
+  0xD801DC2A, /*0xD801DC2A*/ /*U+01042A*/ /*U+01042A*/ /**/
+  0xD801DC2B, /*0xD801DC2B*/ /*U+01042B*/ /*U+01042B*/ /**/
+  0xD801DC2C, /*0xD801DC2C*/ /*U+01042C*/ /*U+01042C*/ /**/
+  0xD801DC2D, /*0xD801DC2D*/ /*U+01042D*/ /*U+01042D*/ /**/
+  0xD801DC2E, /*0xD801DC2E*/ /*U+01042E*/ /*U+01042E*/ /**/
+  0xD801DC2F, /*0xD801DC2F*/ /*U+01042F*/ /*U+01042F*/ /**/
+  0xD801DC30, /*0xD801DC30*/ /*U+010430*/ /*U+010430*/ /**/
+  0xD801DC31, /*0xD801DC31*/ /*U+010431*/ /*U+010431*/ /**/
+  0xD801DC32, /*0xD801DC32*/ /*U+010432*/ /*U+010432*/ /**/
+  0xD801DC33, /*0xD801DC33*/ /*U+010433*/ /*U+010433*/ /**/
+  0xD801DC34, /*0xD801DC34*/ /*U+010434*/ /*U+010434*/ /**/
+  0xD801DC35, /*0xD801DC35*/ /*U+010435*/ /*U+010435*/ /**/
+  0xD801DC36, /*0xD801DC36*/ /*U+010436*/ /*U+010436*/ /**/
+  0xD801DC37, /*0xD801DC37*/ /*U+010437*/ /*U+010437*/ /**/
+  0xD801DC38, /*0xD801DC38*/ /*U+010438*/ /*U+010438*/ /**/
+  0xD801DC39, /*0xD801DC39*/ /*U+010439*/ /*U+010439*/ /**/
+  0xD801DC3A, /*0xD801DC3A*/ /*U+01043A*/ /*U+01043A*/ /**/
+  0xD801DC3B, /*0xD801DC3B*/ /*U+01043B*/ /*U+01043B*/ /**/
+  0xD801DC3C, /*0xD801DC3C*/ /*U+01043C*/ /*U+01043C*/ /**/
+  0xD801DC3D, /*0xD801DC3D*/ /*U+01043D*/ /*U+01043D*/ /**/
+  0xD801DC3E, /*0xD801DC3E*/ /*U+01043E*/ /*U+01043E*/ /**/
+  0xD801DC3F, /*0xD801DC3F*/ /*U+01043F*/ /*U+01043F*/ /**/
+};
+
+/* EOF */
index 26342cb74df39056cdf26f958832d8eabc72ea0d..c3146d0c65caed582f04cea18babfe9fc5fcaf22 100644 (file)
@@ -71,13 +71,12 @@ struct charset_functions charset_utf8_mac =
        NULL, NULL
 };
 
-/* ------------------- Convert from UTF-8 to UCS-2 -------------------*/
+/* ------------------- Convert from UTF-8 to UTF-16 -------------------*/
 static size_t utf8_pull(void *cd _U_, char **inbuf, size_t *inbytesleft,
                         char **outbuf, size_t *outbytesleft)
 {
        ucs2_t uc = 0;
-       ucs2_t hi, low;     /* surrogate pair */
-       unsigned int codepoint, surrogate;
+       unsigned int codepoint;
        int len;
 
        while (*inbytesleft >= 1 && *outbytesleft >= 2) {
@@ -115,10 +114,8 @@ static size_t utf8_pull(void *cd _U_, char **inbuf, size_t *inbytesleft,
                        }
                        codepoint = ((c[0] & 0x07) << 18) | GETUCVAL(c[1],12) |
                                GETUCVAL(c[2],6) |  GETUCVAL(c[3],0);
-                       hi = (ucs2_t)( ((codepoint - 0x10000) >> 10) + 0xD800);
-                       low = (ucs2_t)(0xDC00 + (codepoint & 0x03FF));
-                       surrogate = (hi << 16) | low;
-                       SIVAL(*outbuf,0,surrogate);
+                       SSVAL(*outbuf,0,(((codepoint - 0x10000) >> 10) + 0xD800)); /* hi  */
+                       SSVAL(*outbuf,2,(0xDC00 + (codepoint & 0x03FF)));          /* low */
                        len = 4;
                        (*inbuf)  += 4;
                        (*inbytesleft)  -= 4;
@@ -150,13 +147,13 @@ badseq:
        return -1;
 }
 
-/* --------------------- Convert from UCS-2 to UTF-8 -----------*/
+/* --------------------- Convert from UTF-16 to UTF-8 -----------*/
 static size_t utf8_push(void *cd _U_, char **inbuf, size_t *inbytesleft,
                         char **outbuf, size_t *outbytesleft)
 {
        ucs2_t uc=0;
        ucs2_t hi, low;
-       unsigned int surrogatepair, codepoint;
+       unsigned int codepoint;
        int olen, ilen;
 
        while (*inbytesleft >= 2 && *outbytesleft >= 1) {
@@ -205,9 +202,8 @@ static size_t utf8_push(void *cd _U_, char **inbuf, size_t *inbytesleft,
                                errno = EINVAL;
                                return -1;
                        }
-                       surrogatepair = IVAL((*inbuf),0);
-                       low = (ucs2_t)surrogatepair;
-                       hi = (ucs2_t)(surrogatepair >> 16);
+                       hi =  SVAL((*inbuf),0);
+                       low = SVAL((*inbuf),2);
                        if ( 0xd800 <= hi && hi <= 0xdbff && 0xdc00 <= low && low <= 0xdfff) {
                                codepoint = ((hi - 0xd800) << 10) + (low - 0xdc00) + 0x10000;
                                c[3] = GETUTF8TRAILBYTE(codepoint, 0);
index 45ffcd93f19db8a9ccb0e6ee9ec942c83eac16c8..070ca93b1a99aedb24dc5fc510d302c26fc40fe3 100644 (file)
 #include <netatalk/endian.h>
 
 #include <atalk/unicode.h>
-#include "ucs2_casetable.h"
 #include "precompose.h"
 #include "byteorder.h"
 
-#define HANGUL_SBASE 0xAC00
-#define HANGUL_LBASE 0x1100
-#define HANGUL_VBASE 0x1161
-#define HANGUL_TBASE 0x11A7
-#define HANGUL_LCOUNT 19
-#define HANGUL_VCOUNT 21
-#define HANGUL_TCOUNT 28
-#define HANGUL_NCOUNT (HANGUL_VCOUNT * HANGUL_TCOUNT)   /* 588 */
-#define HANGUL_SCOUNT (HANGUL_LCOUNT * HANGUL_NCOUNT)   /* 11172 */
-
-#define MAXCOMBLEN 3
-
-ucs2_t toupper_w(ucs2_t val)
-{
-       if ( val >= 0x0040 && val <= 0x007F)
-               return upcase_table_1[val-0x0040];
-       if ( val >= 0x00C0 && val <= 0x02BF)
-               return upcase_table_2[val-0x00C0];
-       if ( val >= 0x0380 && val <= 0x04FF)
-               return upcase_table_3[val-0x0380];
-       if ( val >= 0x0540 && val <= 0x05BF)
-               return upcase_table_4[val-0x0540];
-       if ( val >= 0x1E00 && val <= 0x1FFF)
-               return upcase_table_5[val-0x1E00];
-       if ( val >= 0x2140 && val <= 0x217F)
-               return upcase_table_6[val-0x2140];
-       if ( val >= 0x24C0 && val <= 0x24FF)
-               return upcase_table_7[val-0x24C0];
-       if ( val >= 0xFF40 && val <= 0xFF7F)
-               return upcase_table_8[val-0xFF40];
-
-       return (val);
-}
-
-
-ucs2_t tolower_w(ucs2_t val)
-{
-       if ( val >= 0x0040 && val <= 0x007F)
-               return lowcase_table_1[val-0x0040];
-       if ( val >= 0x00C0 && val <= 0x023F)
-               return lowcase_table_2[val-0x00C0];
-       if ( val >= 0x0380 && val <= 0x057F)
-               return lowcase_table_3[val-0x0380];
-       if ( val >= 0x1E00 && val <= 0x1FFF)
-               return lowcase_table_4[val-0x1E00];
-       if ( val >= 0x2140 && val <= 0x217F)
-               return lowcase_table_5[val-0x2140];
-       if ( val >= 0x2480 && val <= 0x24FF)
-               return lowcase_table_6[val-0x2480];
-       if ( val >= 0xFF00 && val <= 0xFF3F)
-               return lowcase_table_7[val-0xFF00];
-
-       return (val);
-}
-
 /*******************************************************************
  Convert a string to lower case.
  return True if any char is converted
 ********************************************************************/
 int strlower_w(ucs2_t *s)
 {
-        int ret = 0;
-        while (*s) {
-                ucs2_t v = tolower_w(*s);
-                if (v != *s) {
-                        *s = v;
-                        ret = 1;
-                }
-                s++;
-        }
-        return ret;
+       int ret = 0;
+       while (*s) {
+               ucs2_t v = tolower_w(*s);
+               if (v != *s) {
+                       *s = v;
+                       ret = 1;
+               }
+               s++;
+       }
+       return ret;
 }
 
 /*******************************************************************
@@ -96,16 +40,16 @@ int strlower_w(ucs2_t *s)
 ********************************************************************/
 int strupper_w(ucs2_t *s)
 {
-        int ret = 0;
-        while (*s) {
-                ucs2_t v = toupper_w(*s);
-                if (v != *s) {
-                        *s = v;
-                        ret = 1;
-                }
-                s++;
-        }
-        return ret;
+       int ret = 0;
+       while (*s) {
+               ucs2_t v = toupper_w(*s);
+               if (v != *s) {
+                       *s = v;
+                       ret = 1;
+               }
+               s++;
+       }
+       return ret;
 }
 
 
@@ -179,18 +123,18 @@ ucs2_t *strcasechr_w(const ucs2_t *s, ucs2_t c)
 
 int strcmp_w(const ucs2_t *a, const ucs2_t *b)
 {
-        while (*b && *a == *b) { a++; b++; }
-        return (*a - *b);
-        /* warning: if *a != *b and both are not 0 we retrun a random
-                greater or lesser than 0 number not realted to which
-                string is longer */
+       while (*b && *a == *b) { a++; b++; }
+       return (*a - *b);
+       /* warning: if *a != *b and both are not 0 we retrun a random
+          greater or lesser than 0 number not realted to which
+          string is longer */
 }
 
 int strncmp_w(const ucs2_t *a, const ucs2_t *b, size_t len)
 {
-        size_t n = 0;
-        while ((n < len) && *b && *a == *b) { a++; b++; n++;}
-        return (len - n)?(*a - *b):0;
+       size_t n = 0;
+       while ((n < len) && *b && *a == *b) { a++; b++; n++;}
+       return (len - n)?(*a - *b):0;
 }
 
 /*******************************************************************
@@ -236,8 +180,8 @@ case insensitive string comparison
 ********************************************************************/
 int strcasecmp_w(const ucs2_t *a, const ucs2_t *b)
 {
-        while (*b && toupper_w(*a) == toupper_w(*b)) { a++; b++; }
-        return (tolower_w(*a) - tolower_w(*b));
+       while (*b && toupper_w(*a) == toupper_w(*b)) { a++; b++; }
+       return (tolower_w(*a) - tolower_w(*b));
 }
 
 /*******************************************************************
@@ -245,9 +189,9 @@ case insensitive string comparison, lenght limited
 ********************************************************************/
 int strncasecmp_w(const ucs2_t *a, const ucs2_t *b, size_t len)
 {
-        size_t n = 0;
-        while ((n < len) && *b && (toupper_w(*a) == toupper_w(*b))) { a++; b++; n++; }
-        return (len - n)?(tolower_w(*a) - tolower_w(*b)):0;
+       size_t n = 0;
+       while ((n < len) && *b && (toupper_w(*a) == toupper_w(*b))) { a++; b++; n++; }
+       return (len - n)?(tolower_w(*a) - tolower_w(*b)):0;
 }
 
 /*******************************************************************
@@ -256,24 +200,24 @@ duplicate string
 /* if len == 0 then duplicate the whole string */
 ucs2_t *strndup_w(const ucs2_t *src, size_t len)
 {
-        ucs2_t *dest;
+       ucs2_t *dest;
 
-        if (!len) len = strlen_w(src);
-        dest = (ucs2_t *)malloc((len + 1) * sizeof(ucs2_t));
-        if (!dest) {
-                LOG (log_error, logtype_default, "strdup_w: out of memory!");
-                return NULL;
-        }
+       if (!len) len = strlen_w(src);
+       dest = (ucs2_t *)malloc((len + 1) * sizeof(ucs2_t));
+       if (!dest) {
+               LOG (log_error, logtype_default, "strdup_w: out of memory!");
+               return NULL;
+       }
 
-        memcpy(dest, src, len * sizeof(ucs2_t));
-        dest[len] = 0;
+       memcpy(dest, src, len * sizeof(ucs2_t));
+       dest[len] = 0;
 
-        return dest;
+       return dest;
 }
 
 ucs2_t *strdup_w(const ucs2_t *src)
 {
-        return strndup_w(src, 0);
+       return strndup_w(src, 0);
 }
 
 /*******************************************************************
@@ -282,16 +226,16 @@ copy a string with max len
 
 ucs2_t *strncpy_w(ucs2_t *dest, const ucs2_t *src, const size_t max)
 {
-        size_t len;
+       size_t len;
 
-        if (!dest || !src) return NULL;
+       if (!dest || !src) return NULL;
 
-        for (len = 0; (src[len] != 0) && (len < max); len++)
-                dest[len] = src[len];
-        while (len < max)
-                dest[len++] = 0;
+       for (len = 0; (src[len] != 0) && (len < max); len++)
+               dest[len] = src[len];
+       while (len < max)
+               dest[len++] = 0;
 
-        return dest;
+       return dest;
 }
 
 
@@ -301,261 +245,383 @@ append a string of len bytes and add a terminator
 
 ucs2_t *strncat_w(ucs2_t *dest, const ucs2_t *src, const size_t max)
 {
-        size_t start;
-        size_t len;
+       size_t start;
+       size_t len;
 
-        if (!dest || !src) return NULL;
+       if (!dest || !src) return NULL;
 
-        start = strlen_w(dest);
-        len = strnlen_w(src, max);
+       start = strlen_w(dest);
+       len = strnlen_w(src, max);
 
-        memcpy(&dest[start], src, len*sizeof(ucs2_t));
-        dest[start+len] = 0;
+       memcpy(&dest[start], src, len*sizeof(ucs2_t));
+       dest[start+len] = 0;
 
-        return dest;
+       return dest;
 }
 
 
 ucs2_t *strcat_w(ucs2_t *dest, const ucs2_t *src)
 {
-        size_t start;
-        size_t len;
+       size_t start;
+       size_t len;
 
-        if (!dest || !src) return NULL;
+       if (!dest || !src) return NULL;
 
-        start = strlen_w(dest);
-        len = strlen_w(src);
+       start = strlen_w(dest);
+       len = strlen_w(src);
 
-        memcpy(&dest[start], src, len*sizeof(ucs2_t));
-        dest[start+len] = 0;
+       memcpy(&dest[start], src, len*sizeof(ucs2_t));
+       dest[start+len] = 0;
 
-        return dest;
+       return dest;
 }
 
 
-/* ------------------------ */
+/*******************************************************************
+binary search for pre|decomposition
+********************************************************************/
+
 static ucs2_t do_precomposition(unsigned int base, unsigned int comb) 
 {
-       int min = 0;
-       int max = sizeof(precompositions) / sizeof(precompositions[0]) - 1;
-       int mid;
-       u_int32_t sought = (base << 16) | comb, that;
-
-       /* binary search */
-       while (max >= min) {
-               mid = (min + max) / 2;
-               that = (precompositions[mid].base << 16) | (precompositions[mid].comb);
-               if (that < sought) {
-                       min = mid + 1;
-               } else if (that > sought) {
-                       max = mid - 1;
-               } else {
-                       return precompositions[mid].replacement;
-               }
-       }
-       /* no match */
-       return 0;
+       int min = 0;
+       int max = PRECOMP_COUNT - 1;
+       int mid;
+       u_int32_t sought = (base << 16) | comb, that;
+
+       /* binary search */
+       while (max >= min) {
+               mid = (min + max) / 2;
+               that = (precompositions[mid].base << 16) | (precompositions[mid].comb);
+               if (that < sought) {
+                       min = mid + 1;
+               } else if (that > sought) {
+                       max = mid - 1;
+               } else {
+                       return precompositions[mid].replacement;
+               }
+       }
+       /* no match */
+       return 0;
+}
+
+/* ------------------------ */
+static u_int32_t do_precomposition_sp(unsigned int base_sp, unsigned int comb_sp) 
+{
+       int min = 0;
+       int max = PRECOMP_SP_COUNT - 1;
+       int mid;
+       u_int64_t sought_sp = ((u_int64_t)base_sp << 32) | (u_int64_t)comb_sp, that_sp;
+
+       /* binary search */
+       while (max >= min) {
+               mid = (min + max) / 2;
+               that_sp = ((u_int64_t)precompositions_sp[mid].base_sp << 32) | ((u_int64_t)precompositions_sp[mid].comb_sp);
+               if (that_sp < sought_sp) {
+                       min = mid + 1;
+               } else if (that_sp > sought_sp) {
+                       max = mid - 1;
+               } else {
+                       return precompositions_sp[mid].replacement_sp;
+               }
+       }
+       /* no match */
+       return 0;
 }
 
 /* -------------------------- */
 static u_int32_t do_decomposition(ucs2_t base) 
 {
-       int min = 0;
-       int max = sizeof(decompositions) / sizeof(decompositions[0]) - 1;
-       int mid;
-       u_int32_t sought = base;
-       u_int32_t result, that;
-
-       /* binary search */
-       while (max >= min) {
-               mid = (min + max) / 2;
-               that = decompositions[mid].replacement;
-               if (that < sought) {
-                       min = mid + 1;
-               } else if (that > sought) {
-                       max = mid - 1;
-               } else {
-                       result = (decompositions[mid].base << 16) | (decompositions[mid].comb);
-                       return result;
-               }
-       }
-       /* no match */
-       return 0;
+       int min = 0;
+       int max = DECOMP_COUNT - 1;
+       int mid;
+       u_int32_t sought = base;
+       u_int32_t result, that;
+
+       /* binary search */
+       while (max >= min) {
+               mid = (min + max) / 2;
+               that = decompositions[mid].replacement;
+               if (that < sought) {
+                       min = mid + 1;
+               } else if (that > sought) {
+                       max = mid - 1;
+               } else {
+                       result = (decompositions[mid].base << 16) | (decompositions[mid].comb);
+                       return result;
+               }
+       }
+       /* no match */
+       return 0;
+}
+
+/* -------------------------- */
+static u_int64_t do_decomposition_sp(unsigned int base_sp) 
+{
+       int min = 0;
+       int max = DECOMP_SP_COUNT - 1;
+       int mid;
+       u_int32_t sought_sp = base_sp;
+       u_int32_t that_sp;
+       u_int64_t result_sp;
+
+       /* binary search */
+       while (max >= min) {
+               mid = (min + max) / 2;
+               that_sp = decompositions_sp[mid].replacement_sp;
+               if (that_sp < sought_sp) {
+                       min = mid + 1;
+               } else if (that_sp > sought_sp) {
+                       max = mid - 1;
+               } else {
+                       result_sp = ((u_int64_t)decompositions_sp[mid].base_sp << 32) | ((u_int64_t)decompositions_sp[mid].comb_sp);
+                       return result_sp;
+               }
+       }
+       /* no match */
+       return 0;
 }
 
-/* we can't use static, this stuff needs to be reentrant */
-/* static char comp[MAXPATHLEN +1]; */
+/*******************************************************************
+pre|decomposition
+
+   we can't use static, this stuff needs to be reentrant
+   static char comp[MAXPATHLEN +1];
+
+   We don't implement Singleton and Canonical Ordering.
+   We ignore CompositionExclusions.txt.
+   because they cause the problem of the roundtrip
+   such as Dancing Icon.
+
+   exclude U2000-U2FFF, UFE30-UFE4F and U2F800-U2FA1F ranges
+   in precompose.h from composition according to AFP 3.x spec
+********************************************************************/
 
 size_t precompose_w (ucs2_t *name, size_t inplen, ucs2_t *comp, size_t *outlen)
 {
        size_t i;
        ucs2_t base, comb;
+       u_int32_t base_sp, comb_sp;
        ucs2_t *in, *out;
-       ucs2_t hangul_lindex, hangul_vindex;
+       ucs2_t lindex, vindex;
        ucs2_t result;
+       u_int32_t result_sp;
        size_t o_len = *outlen;
-
-       if (!inplen || (inplen & 1) || inplen > o_len)
-               return (size_t)-1;
        
-       /*   Actually,                                                 */
-       /*   Decomposition and Canonical Ordering are necessary here.  */
-       /*                                                             */
-       /*         Ex. in = CanonicalOrdering(decompose_w(name))       */
-       /*                                                             */
-       /*   A new mapping table is needed for CanonicalOrdering.      */
+       if (!inplen || (inplen & 1) || inplen > o_len)
+               return (size_t)-1;
        
-       i = 0;
-       in  = name;
+       i = 0;
+       in  = name;
        out = comp;
-    
-       base = *in;
-       while (*outlen > 2) {
-               i += 2;
-               in++;
-               if (i == inplen) {
-                       *out = base;
+       
+       base = *in;
+       while (*outlen > 2) {
+               i += 2;
+               in++;
+
+               if (i == inplen) {
+                       *out = base;
                        out++;
                        *out = 0;
-                       *outlen -= 2;
-                       return o_len - *outlen;
-               }
-               comb = *in;
+                       *outlen -= 2;
+                       return o_len - *outlen;
+               }
+
+               comb = *in;
                result = 0;
-               
+
                /* Non-Combination Character */
                if (comb < 0x300) ;
                
                /* Unicode Standard Annex #15 A10.3 Hangul Composition */
                /* Step 1 <L,V> */
-               else if ((HANGUL_VBASE <= comb) && (comb <= HANGUL_VBASE + HANGUL_VCOUNT)) {
-                       if ((HANGUL_LBASE <= base) && (base < HANGUL_LBASE + HANGUL_LCOUNT)) {
+               else if ((VBASE <= comb) && (comb <= VBASE + VCOUNT)) {
+                       if ((LBASE <= base) && (base < LBASE + LCOUNT)) {
                                result = 1;
-                               hangul_lindex = base - HANGUL_LBASE;
-                               hangul_vindex = comb - HANGUL_VBASE;
-                               base = HANGUL_SBASE + (hangul_lindex * HANGUL_VCOUNT + hangul_vindex) * HANGUL_TCOUNT;
+                               lindex = base - LBASE;
+                               vindex = comb - VBASE;
+                               base = SBASE + (lindex * VCOUNT + vindex) * TCOUNT;
                        }
-               }
+               }
                
                /* Step 2 <LV,T> */
-               else if ((HANGUL_TBASE < comb) && (comb < HANGUL_TBASE + HANGUL_TCOUNT)) {
-                       if ((HANGUL_SBASE <= base) && (base < HANGUL_SBASE +HANGUL_SCOUNT) && (((base - HANGUL_SBASE) % HANGUL_TCOUNT) == 0)) {
+               else if ((TBASE < comb) && (comb < TBASE + TCOUNT)) {
+                       if ((SBASE <= base) && (base < SBASE + SCOUNT) && (((base - SBASE) % TCOUNT) == 0)) {
                                result = 1;
-                               base += comb - HANGUL_TBASE;
+                               base += comb - TBASE;
                        }
                }
                
-               /* Combining Sequence */
-               else if ((result = do_precomposition(base, comb))) {
+               /* Binary Search for Surrogate Pair */
+               else if ((0xD800 <= base) && (base < 0xDC00)) {
+                       if ((0xDC00 <= comb) && (comb < 0xE000) && (i + 4 <= inplen)) {
+                               base_sp = ((u_int32_t)base << 16) | (u_int32_t)comb;
+                               do {
+                                       comb_sp = ((u_int32_t)in[1] << 16) | (u_int32_t)in[2];
+                                       if (result_sp = do_precomposition_sp(base_sp, comb_sp)) {
+                                               base_sp = result_sp;
+                                               i += 4;
+                                               in +=2;
+                                       }
+                               } while ((i + 4 <= inplen) && result_sp) ;
+
+                               *out = base_sp >> 16;
+                               out++;
+                               *outlen -= 2;
+
+                               if (*outlen <= 2) {
+                                       errno = E2BIG;
+                                       return (size_t)-1;
+                               }
+
+                               *out = base_sp & 0xFFFF;
+                               out++;
+                               *outlen -= 2;
+
+                               i += 2;
+                               in++;
+                               base = *in;
+
+                               result = 1;
+                       }
+               }
+
+               /* Binary Search for BMP */
+               else if (result = do_precomposition(base, comb)) {
                        base = result;
                }
                
                if (!result) {
-                       *out = base;
-                       out++;
-                       *outlen -= 2;
-                       base = comb;
-               }
-       }
-       
+                       *out = base;
+                       out++;
+                       *outlen -= 2;
+                       base = comb;
+               }
+       }
+
        errno = E2BIG;
        return (size_t)-1;
 }
 
 /* --------------- */
-
-/* Singleton Decomposition is unsupported.               */
-/* A new mapping table is needed for implementation.     */
-
 size_t decompose_w (ucs2_t *name, size_t inplen, ucs2_t *comp, size_t *outlen)
 {
        size_t i;
        size_t comblen;
-       ucs2_t base;
-       ucs2_t comb[MAXCOMBLEN];
-       ucs2_t hangul_sindex, tjamo;
+       ucs2_t base, comb[COMBBUFLEN];
+       u_int32_t base_sp;
+       ucs2_t sindex, tjamo;
        ucs2_t *in, *out;
        unsigned int result;
+       u_int64_t result_sp;
        size_t o_len = *outlen;
 
-       if (!inplen || (inplen & 1))
-               return (size_t)-1;
+       if (!inplen || (inplen & 1))
+               return (size_t)-1;
        i = 0;
        in  = name;
        out = comp;
-    
-       while (i < inplen) {
-               base = *in;
+
+       while (i < inplen) {
+               base = *in;
                comblen = 0;
                
                /* check ASCII first. this is frequent. */
                if (base <= 0x007f) ;
                
                /* Unicode Standard Annex #15 A10.2 Hangul Decomposition */
-               else if ((HANGUL_SBASE <= base) && (base < HANGUL_SBASE + HANGUL_SCOUNT)) {
-                       hangul_sindex = base - HANGUL_SBASE;
-                       base = HANGUL_LBASE + hangul_sindex / HANGUL_NCOUNT;
-                       comb[MAXCOMBLEN-2] = HANGUL_VBASE + (hangul_sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT;
+               else if ((SBASE <= base) && (base < SBASE + SCOUNT)) {
+                       sindex = base - SBASE;
+                       base = LBASE + sindex / NCOUNT;
+                       comb[COMBBUFLEN-2] = VBASE + (sindex % NCOUNT) / TCOUNT;
                        
                        /* <L,V> */
-                       if ((tjamo = HANGUL_TBASE + hangul_sindex % HANGUL_TCOUNT) == HANGUL_TBASE) {
-                               comb[MAXCOMBLEN-1] = comb[MAXCOMBLEN-2];
+                       if ((tjamo = TBASE + sindex % TCOUNT) == TBASE) {
+                               comb[COMBBUFLEN-1] = comb[COMBBUFLEN-2];
                                comblen = 1;
                        }
                        
                        /* <L,V,T> */
                        else {
-                               comb[MAXCOMBLEN-1] = tjamo;
+                               comb[COMBBUFLEN-1] = tjamo;
                                comblen = 2;
                        }
                }
                
-               /* Combining Sequence */
-               /* exclude U2000-U2FFF and UFE30-UFE4F ranges in decompositions[]     */
-               /* from decomposition according to AFP 3.1 spec    */
+               /* Binary Search for Surrogate Pair */
+               else if ((0xD800 <= base) && (base < 0xDC00)) {
+                       if (i + 2 < inplen) {
+                               base_sp =  ((u_int32_t)base << 16) | (u_int32_t)in[1];
+                               do {
+                                       if ( !(result_sp = do_decomposition_sp(base_sp))) break;
+                                       comblen += 2;
+                                       base_sp = result_sp >> 32;
+                                       comb[COMBBUFLEN-comblen] = (result_sp >> 16) & 0xFFFF;  /* hi */
+                                       comb[COMBBUFLEN-comblen+1] = result_sp & 0xFFFF;        /* lo */
+                               } while (comblen < MAXCOMBSPLEN);
+
+                               if (*outlen < (comblen + 1) << 1) {
+                                       errno = E2BIG;
+                                       return (size_t)-1;
+                               }
+
+                               *out = base_sp >> 16;   /* hi */
+                               out++;
+                               *outlen -= 2;
+                               
+                               base = base_sp & 0xFFFF; /* lo */
+                               
+                               i += 2;
+                               in++;
+                       }
+               }
+                       
+               /* Binary Search for BMP */
                else {
                        do {
-                               if ((comblen >= MAXCOMBLEN) || !(result = do_decomposition(base))) break;
+                               if ( !(result = do_decomposition(base))) break;
                                comblen++;
                                base = result  >> 16;
-                               comb[MAXCOMBLEN-comblen] = result & 0xffff;
-                       } while (0x007f < base) ;
+                               comb[COMBBUFLEN-comblen] = result & 0xFFFF;
+                       } while ((0x007f < base) && (comblen < MAXCOMBLEN));
                }
                
                if (*outlen < (comblen + 1) << 1) {
-                               errno = E2BIG;
-                               return (size_t)-1;
-                       }
+                       errno = E2BIG;
+                       return (size_t)-1;
+               }
                
                *out = base;
-                       out++;
-                       *outlen -= 2;
+               out++;
+               *outlen -= 2;
                
                while ( comblen > 0 ) {
-                       *out = comb[MAXCOMBLEN-comblen];
-                       out++;
-                       *outlen -= 2;
+                       *out = comb[COMBBUFLEN-comblen];
+                       out++;
+                       *outlen -= 2;
                        comblen--;
-               }
+               }
                
-               i += 2;
-               in++;
-       }
-
-       /* Is Canonical Ordering necessary here? */
+               i += 2;
+               in++;
+       }
        
        *out = 0;
        return o_len-*outlen;
 }
 
+/*******************************************************************
+length of UTF-8 character and string
+********************************************************************/
+
 size_t utf8_charlen ( char* utf8 )
 {
-        unsigned char *p;
+       unsigned char *p;
 
-        p = (unsigned char*) utf8;
+       p = (unsigned char*) utf8;
        
        if ( *p < 0x80 )
-               return (1);
+               return (1);
        else if ( *p > 0xC1 && *p < 0xe0 && *(p+1) > 0x7f && *(p+1) < 0xC0)
                return (2);
        else if ( *p == 0xe0 && *(p+1) > 0x9f && *(p+1) < 0xc0 && *(p+2) > 0x7f && *(p+2) < 0xc0)
@@ -575,43 +641,42 @@ size_t utf8_charlen ( char* utf8 )
 
 size_t utf8_strlen_validate ( char * utf8 )
 {
-        size_t len;
-        unsigned char *p;
+       size_t len;
+       unsigned char *p;
 
-        p = (unsigned char*) utf8;
-        len = 0;
+       p = (unsigned char*) utf8;
+       len = 0;
 
-        /* see http://www.unicode.org/unicode/reports/tr27/ for an explanation */
+       /* see http://www.unicode.org/unicode/reports/tr27/ for an explanation */
 
-        while ( *p != '\0')
-        {
-                if ( *p < 0x80 )
-                        p++;
+       while ( *p != '\0')
+       {
+               if ( *p < 0x80 )
+                       p++;
 
-                else if ( *p > 0xC1 && *p < 0xe0 && *(p+1) > 0x7f && *(p+1) < 0xC0)
-                        p += 2;
+               else if ( *p > 0xC1 && *p < 0xe0 && *(p+1) > 0x7f && *(p+1) < 0xC0)
+                       p += 2;
 
-                else if ( *p == 0xe0 && *(p+1) > 0x9f && *(p+1) < 0xc0 && *(p+2) > 0x7f && *(p+2) < 0xc0)
-                        p += 3;
+               else if ( *p == 0xe0 && *(p+1) > 0x9f && *(p+1) < 0xc0 && *(p+2) > 0x7f && *(p+2) < 0xc0)
+                       p += 3;
 
-                else if ( *p > 0xe0  && *p < 0xf0 && *(p+1) > 0x7f && *(p+1) < 0xc0 && *(p+2) > 0x7f && *(p+2) < 0xc0)
-                        p += 3;
+               else if ( *p > 0xe0  && *p < 0xf0 && *(p+1) > 0x7f && *(p+1) < 0xc0 && *(p+2) > 0x7f && *(p+2) < 0xc0)
+                       p += 3;
 
-                else if ( *p == 0xf0 && *(p+1) > 0x8f && *(p+1) < 0xc0 && *(p+2) > 0x7f && *(p+2) < 0xc0 && *(p+3) > 0x7f && *(p+3) < 0xc0 )
-                        p += 4;
+               else if ( *p == 0xf0 && *(p+1) > 0x8f && *(p+1) < 0xc0 && *(p+2) > 0x7f && *(p+2) < 0xc0 && *(p+3) > 0x7f && *(p+3) < 0xc0 )
+                       p += 4;
 
-                else if ( *p > 0xf0 && *p < 0xf4 && *(p+1) > 0x7f && *(p+1) < 0xc0 && *(p+2) > 0x7f && *(p+2) < 0xc0 && *(p+3) > 0x7f && *(p+3) < 0xc0 )
-                        p += 4;
+               else if ( *p > 0xf0 && *p < 0xf4 && *(p+1) > 0x7f && *(p+1) < 0xc0 && *(p+2) > 0x7f && *(p+2) < 0xc0 && *(p+3) > 0x7f && *(p+3) < 0xc0 )
+                       p += 4;
 
-                else if ( *p == 0xf4 && *(p+1) > 0x7f && *(p+1) < 0x90 && *(p+2) > 0x7f && *(p+2) < 0xc0 && *(p+3) > 0x7f && *(p+3) < 0xc0 )
-                        p += 4;
+               else if ( *p == 0xf4 && *(p+1) > 0x7f && *(p+1) < 0x90 && *(p+2) > 0x7f && *(p+2) < 0xc0 && *(p+3) > 0x7f && *(p+3) < 0xc0 )
+                       p += 4;
 
-                else
-                        return ((size_t) -1);
+               else
+                       return ((size_t) -1);
 
-                len++;
-        }
+               len++;
+       }
 
-        return (len);
+       return (len);
 }
-
index f8eeb6b42c9c6e8506d8f6fcd95e713887c96368..af5d3c9ff6b10515d0aaebc26b6350ea3531c022 100644 (file)
@@ -1,7 +1,5 @@
 # Makefile.am for libatalk/util/
 
-SUBDIRS = . test
-
 noinst_LTLIBRARIES = libutil.la
 
 AM_CFLAGS = -I$(top_srcdir)/sys
@@ -9,11 +7,13 @@ AM_CFLAGS = -I$(top_srcdir)/sys
 libutil_la_SOURCES = \
        atalk_addr.c    \
        bprint.c        \
+       cnid.c          \
        fault.c         \
        getiface.c      \
        locking.c   \
        logger.c        \
        module.c        \
+       queue.c     \
        server_child.c  \
        server_ipc.c    \
        server_lock.c   \
@@ -23,3 +23,7 @@ libutil_la_SOURCES = \
        strlcpy.c       \
        volinfo.c \
        unix.c
+
+if HAVE_ATFUNCS
+libutil_la_SOURCES += ftw.c
+endif
diff --git a/libatalk/util/cnid.c b/libatalk/util/cnid.c
new file mode 100644 (file)
index 0000000..3753090
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <string.h>
+#include <libgen.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/volinfo.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
+#include <atalk/logger.h>
+#include <atalk/errchk.h>
+#include <atalk/unicode.h>
+
+/*!
+ * Build path relativ to volume root
+ *
+ * path might be:
+ * (a) relative:
+ *     "dir/subdir" with cwd: "/afp_volume/topdir"
+ * (b) absolute:
+ *     "/afp_volume/dir/subdir"
+ *
+ * @param path     (r) path relative to cwd() or absolute
+ * @param volpath  (r) volume path that path is a subdir of (has been computed in volinfo funcs)
+ *
+ * @returns relative path in new bstring, caller must bdestroy it
+ */
+bstring rel_path_in_vol(const char *path, const char *volpath)
+{
+    EC_INIT;
+    int cwd = -1;
+    bstring fpath = NULL;
+    char *dname = NULL;
+    struct stat st;
+
+    if (path == NULL || volpath == NULL)
+        return NULL;
+
+    EC_NEG1_LOG(cwd = open(".", O_RDONLY));
+
+    EC_ZERO_LOGSTR(lstat(path, &st), "lstat(%s): %s", path, strerror(errno));
+
+    if (path[0] == '/') {
+        EC_NULL(fpath = bfromcstr(path));
+    } else {
+        switch (S_IFMT & st.st_mode) {
+        case S_IFREG:
+        case S_IFLNK:
+            EC_NULL_LOG(dname = strdup(path));
+            EC_ZERO_LOGSTR(chdir(dirname(dname)), "chdir(%s): %s", dirname, strerror(errno));
+            free(dname);
+            dname = NULL;
+            EC_NULL(fpath = bfromcstr(getcwdpath()));
+            BSTRING_STRIP_SLASH(fpath);
+            EC_ZERO(bcatcstr(fpath, "/"));
+            EC_NULL_LOG(dname = strdup(path));
+            EC_ZERO(bcatcstr(fpath, basename(dname)));
+            break;
+
+        case S_IFDIR:
+            EC_ZERO_LOGSTR(chdir(path), "chdir(%s): %s", path, strerror(errno));
+            EC_NULL(fpath = bfromcstr(getcwdpath()));
+            break;
+
+        default:
+            EC_FAIL;
+        }
+    }
+
+    BSTRING_STRIP_SLASH(fpath);
+
+    /*
+     * Now we have eg:
+     *   fpath:   /Volume/netatalk/dir/bla
+     *   volpath: /Volume/netatalk/
+     * we want: "dir/bla"
+     */
+    int len = strlen(volpath);
+    if (volpath[len-1] != '/')
+        /* in case volpath has no trailing slash */
+        len ++;
+    EC_ZERO(bdelete(fpath, 0, len));
+
+EC_CLEANUP:
+    if (dname) free(dname);
+    if (cwd != -1) {
+        fchdir(cwd);
+        close(cwd);
+    }
+    if (ret != 0)
+        return NULL;
+    return fpath;
+}
index 7ef4fb8a5143cac21ddc8c6bc604d60ca82822a2..2b9ef64455f403d34201c7936cc43ef6806e63cd 100644 (file)
@@ -22,8 +22,6 @@
 #include "config.h"
 #endif
 
-#ifdef DEBUG1
-
 #include <sys/types.h>
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
@@ -84,65 +82,28 @@ static void (*CatchSignal(int signum,void (*handler)(int )))(int)
  Something really nasty happened - panic !
 ********************************************************************/
 
-static void smb_panic(const char *why)
+void netatalk_panic(const char *why)
 {
-#if 0
-       char *cmd;
-       int result;
-#endif
 #ifdef HAVE_BACKTRACE_SYMBOLS
        void *backtrace_stack[BACKTRACE_STACK_SIZE];
        size_t backtrace_size;
        char **backtrace_strings;
-#endif
-
-#ifdef DEVELOPER
-       {
-               extern char *global_clobber_region_function;
-               extern unsigned int global_clobber_region_line;
-
-               if (global_clobber_region_function) {
-                       DEBUG(0,("smb_panic: clobber_region() last called from [%s(%u)]",
-                                        global_clobber_region_function,
-                                        global_clobber_region_line));
-               } 
-       }
-#endif
 
-#if 0
-       cmd = lp_panic_action();
-       if (cmd && *cmd) {
-               DEBUG(0, ("smb_panic(): calling panic action [%s]\n", cmd));
-               result = system(cmd);
-
-               if (result == -1)
-                       DEBUG(0, ("smb_panic(): fork failed in panic action: %s\n",
-                                         strerror(errno)));
-               else
-                       DEBUG(0, ("smb_panic(): action returned status %d\n",
-                                         WEXITSTATUS(result)));
-       }
-       DEBUG(0,("PANIC: %s\n", why));
-#endif
-
-#ifdef HAVE_BACKTRACE_SYMBOLS
        /* get the backtrace (stack frames) */
        backtrace_size = backtrace(backtrace_stack,BACKTRACE_STACK_SIZE);
        backtrace_strings = backtrace_symbols(backtrace_stack, backtrace_size);
 
-       LOG(log_error, logtype_default, "BACKTRACE: %d stack frames:\n", backtrace_size);
+       LOG(log_severe, logtype_default, "BACKTRACE: %d stack frames:", backtrace_size);
        
        if (backtrace_strings) {
                size_t i;
 
                for (i = 0; i < backtrace_size; i++)
-                       LOG(log_error, logtype_default, " #%u %s", i, backtrace_strings[i]);
+                       LOG(log_severe, logtype_default, " #%u %s", i, backtrace_strings[i]);
 
                SAFE_FREE(backtrace_strings);
        }
-
 #endif
-
 }
 
 
@@ -153,15 +114,16 @@ static void fault_report(int sig)
 {
        static int counter;
 
-       if (counter) _exit(1);
+       if (counter)
+        abort();
 
        counter++;
 
-       LOG(log_error, logtype_default, "===============================================================");
-       LOG(log_error, logtype_default, "INTERNAL ERROR: Signal %d in pid %d (%s)",sig,(int)getpid(),VERSION);
-       LOG(log_error, logtype_default, "===============================================================");
+       LOG(log_severe, logtype_default, "===============================================================");
+       LOG(log_severe, logtype_default, "INTERNAL ERROR: Signal %d in pid %d (%s)",sig,(int)getpid(),VERSION);
+       LOG(log_severe, logtype_default, "===============================================================");
   
-       smb_panic("internal error");
+       netatalk_panic("internal error");
 
        if (cont_fn) {
                cont_fn(NULL);
@@ -173,7 +135,7 @@ static void fault_report(int sig)
 #endif
                return; /* this should cause a core dump */
        }
-       exit(1);
+    abort();
 }
 
 /****************************************************************************
@@ -199,4 +161,3 @@ void fault_setup(void (*fn)(void *))
 #endif
 }
 
-#endif
diff --git a/libatalk/util/ftw.c b/libatalk/util/ftw.c
new file mode 100644 (file)
index 0000000..3a32c12
--- /dev/null
@@ -0,0 +1,826 @@
+/* File tree walker functions.
+   Copyright (C) 1996-2004, 2006-2008, 2010 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if __GNUC__
+# define alloca __builtin_alloca
+#else
+#  include <alloca.h>
+#endif
+
+#include <dirent.h>
+#define NAMLEN(dirent) strlen ((dirent)->d_name)
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <search.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#if HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+
+#include <atalk/ftw.h>
+
+#define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N)))
+
+#define NDEBUG 1
+#include <assert.h>
+
+#ifndef _LIBC
+# undef __chdir
+# define __chdir chdir
+# undef __closedir
+# define __closedir closedir
+# undef __fchdir
+# define __fchdir fchdir
+# undef __getcwd
+# define __getcwd(P, N) xgetcwd ()
+# undef __mempcpy
+# define __mempcpy mempcpy
+# undef __opendir
+# define __opendir opendir
+# undef __readdir64
+# define __readdir64 readdir
+# undef __tdestroy
+# define __tdestroy tdestroy
+# undef __tfind
+# define __tfind tfind
+# undef __tsearch
+# define __tsearch tsearch
+# undef internal_function
+# define internal_function /* empty */
+# undef dirent64
+# define dirent64 dirent
+# undef MAX
+# define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef __set_errno
+# define __set_errno(Val) errno = (Val)
+#endif
+
+/* Support for the LFS API version.  */
+#ifndef FTW_NAME
+# define FTW_NAME ftw
+# define NFTW_NAME nftw
+# define NFTW_OLD_NAME __old_nftw
+# define NFTW_NEW_NAME __new_nftw
+# define INO_T ino_t
+# define STAT stat
+# define LXSTAT(V,f,sb) lstat (f,sb)
+# define XSTAT(V,f,sb) stat (f,sb)
+# define FXSTATAT(V,d,f,sb,m) fstatat (d, f, sb, m)
+
+#endif
+
+/* We define PATH_MAX if the system does not provide a definition.
+   This does not artificially limit any operation.  PATH_MAX is simply
+   used as a guesstimate for the expected maximal path length.
+   Buffers will be enlarged if necessary.  */
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+struct dir_data
+{
+    DIR *stream;
+    int streamfd;
+    char *content;
+};
+
+struct known_object
+{
+    dev_t dev;
+    INO_T ino;
+};
+
+struct ftw_data
+{
+    /* Array with pointers to open directory streams.  */
+    struct dir_data **dirstreams;
+    size_t actdir;
+    size_t maxdir;
+
+    /* Buffer containing name of currently processed object.  */
+    char *dirbuf;
+    size_t dirbufsize;
+
+    /* Passed as fourth argument to `nftw' callback.  The `base' member
+       tracks the content of the `dirbuf'.  */
+    struct FTW ftw;
+
+    /* Flags passed to `nftw' function.  0 for `ftw'.  */
+    int flags;
+
+    /* Conversion array for flag values.  It is the identity mapping for
+       `nftw' calls, otherwise it maps the values to those known by
+       `ftw'.  */
+    const int *cvt_arr;
+
+    /* Callback function.  We always use the `nftw' form.  */
+    NFTW_FUNC_T func;
+
+    /* Device of starting point.  Needed for FTW_MOUNT.  */
+    dev_t dev;
+
+    /* Data structure for keeping fingerprints of already processed
+       object.  This is needed when not using FTW_PHYS.  */
+    void *known_objects;
+};
+
+
+/* Internally we use the FTW_* constants used for `nftw'.  When invoked
+   as `ftw', map each flag to the subset of values used by `ftw'.  */
+static const int nftw_arr[] =
+{
+    FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_SL, FTW_DP, FTW_SLN
+};
+
+static const int ftw_arr[] =
+{
+    FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_F, FTW_D, FTW_NS
+};
+
+
+static dir_notification_func_t upfunc;
+
+/* Forward declarations of local functions.  */
+static int ftw_dir (struct ftw_data *data, struct STAT *st,
+                    struct dir_data *old_dir) internal_function;
+
+typedef void (*__free_fn_t) (void *__nodep);
+typedef struct node_t {
+    const void *key;
+    struct node_t *left;
+    struct node_t *right;
+    unsigned int red:1;
+} *node;
+
+static void tdestroy_recurse (node root, __free_fn_t freefct)
+{
+    if (root->left != NULL)
+        tdestroy_recurse (root->left, freefct);
+    if (root->right != NULL)
+        tdestroy_recurse (root->right, freefct);
+    (*freefct) ((void *) root->key);
+    /* Free the node itself.  */
+    free (root);
+}
+
+static void mytdestroy (void *vroot, __free_fn_t freefct)
+{
+    node root = (node) vroot;
+
+    if (root != NULL)
+        tdestroy_recurse (root, freefct);
+}
+
+static char *mystpcpy(char *a, const char *b)
+{
+    strcpy(a, b);
+    return (a + strlen(a));
+}
+
+static char *xgetcwd(void)
+{
+    char *cwd;
+    char *ret;
+    unsigned path_max;
+
+    errno = 0;
+    path_max = (unsigned) PATH_MAX;
+    path_max += 2;        /* The getcwd docs say to do this. */
+
+    cwd = malloc (path_max);
+    errno = 0;
+    while ((ret = getcwd (cwd, path_max)) == NULL && errno == ERANGE) {
+        path_max += 512;
+        cwd = realloc (cwd, path_max);
+        errno = 0;
+    }
+
+    if (ret == NULL) {
+        int save_errno = errno;
+        free (cwd);
+        errno = save_errno;
+        return NULL;
+    }
+    return cwd;
+}
+
+static int
+object_compare (const void *p1, const void *p2)
+{
+    /* We don't need a sophisticated and useful comparison.  We are only
+       interested in equality.  However, we must be careful not to
+       accidentally compare `holes' in the structure.  */
+    const struct known_object *kp1 = p1, *kp2 = p2;
+    int cmp1;
+    cmp1 = (kp1->ino > kp2->ino) - (kp1->ino < kp2->ino);
+    if (cmp1 != 0)
+        return cmp1;
+    return (kp1->dev > kp2->dev) - (kp1->dev < kp2->dev);
+}
+
+
+static int
+add_object (struct ftw_data *data, struct STAT *st)
+{
+    struct known_object *newp = malloc (sizeof (struct known_object));
+    if (newp == NULL)
+        return -1;
+    newp->dev = st->st_dev;
+    newp->ino = st->st_ino;
+    return __tsearch (newp, &data->known_objects, object_compare) ? 0 : -1;
+}
+
+
+static inline int
+find_object (struct ftw_data *data, struct STAT *st)
+{
+    struct known_object obj;
+    obj.dev = st->st_dev;
+    obj.ino = st->st_ino;
+    return __tfind (&obj, &data->known_objects, object_compare) != NULL;
+}
+
+
+static inline int
+open_dir_stream (int *dfdp, struct ftw_data *data, struct dir_data *dirp)
+{
+    int result = 0;
+
+    if (data->dirstreams[data->actdir] != NULL)
+    {
+        /* Oh, oh.  We must close this stream.  Get all remaining
+           entries and store them as a list in the `content' member of
+           the `struct dir_data' variable.  */
+        size_t bufsize = 1024;
+        char *buf = malloc (bufsize);
+
+        if (buf == NULL)
+            result = -1;
+        else
+        {
+            DIR *st = data->dirstreams[data->actdir]->stream;
+            struct dirent64 *d;
+            size_t actsize = 0;
+
+            while ((d = __readdir64 (st)) != NULL)
+            {
+                size_t this_len = NAMLEN (d);
+                if (actsize + this_len + 2 >= bufsize)
+                {
+                    char *newp;
+                    bufsize += MAX (1024, 2 * this_len);
+                    newp = (char *) realloc (buf, bufsize);
+                    if (newp == NULL)
+                    {
+                        /* No more memory.  */
+                        int save_err = errno;
+                        free (buf);
+                        __set_errno (save_err);
+                        return -1;
+                    }
+                    buf = newp;
+                }
+
+                *((char *) __mempcpy (buf + actsize, d->d_name, this_len))
+                    = '\0';
+                actsize += this_len + 1;
+            }
+
+            /* Terminate the list with an additional NUL byte.  */
+            buf[actsize++] = '\0';
+
+            /* Shrink the buffer to what we actually need.  */
+            data->dirstreams[data->actdir]->content = realloc (buf, actsize);
+            if (data->dirstreams[data->actdir]->content == NULL)
+            {
+                int save_err = errno;
+                free (buf);
+                __set_errno (save_err);
+                result = -1;
+            }
+            else
+            {
+                __closedir (st);
+                data->dirstreams[data->actdir]->stream = NULL;
+                data->dirstreams[data->actdir]->streamfd = -1;
+                data->dirstreams[data->actdir] = NULL;
+            }
+        }
+    }
+
+    /* Open the new stream.  */
+    if (result == 0)
+    {
+        assert (data->dirstreams[data->actdir] == NULL);
+
+        if (dfdp != NULL && *dfdp != -1)
+        {
+            int fd = openat(*dfdp, data->dirbuf + data->ftw.base, O_RDONLY);
+            dirp->stream = NULL;
+            if (fd != -1 && (dirp->stream = fdopendir (fd)) == NULL)
+                close(fd);
+        }
+        else
+        {
+            const char *name;
+
+            if (data->flags & FTW_CHDIR)
+            {
+                name = data->dirbuf + data->ftw.base;
+                if (name[0] == '\0')
+                    name = ".";
+            }
+            else
+                name = data->dirbuf;
+
+            dirp->stream = __opendir (name);
+        }
+
+        if (dirp->stream == NULL)
+            result = -1;
+        else
+        {
+            dirp->streamfd = dirfd (dirp->stream);
+            dirp->content = NULL;
+            data->dirstreams[data->actdir] = dirp;
+
+            if (++data->actdir == data->maxdir)
+                data->actdir = 0;
+        }
+    }
+
+    return result;
+}
+
+
+static int
+process_entry (struct ftw_data *data, struct dir_data *dir, const char *name, size_t namlen)
+{
+    struct STAT st;
+    int result = 0;
+    int flag = 0;
+    size_t new_buflen;
+
+    if (name[0] == '.' && (name[1] == '\0'
+                           || (name[1] == '.' && name[2] == '\0')))
+        /* Don't process the "." and ".." entries.  */
+        return 0;
+
+    new_buflen = data->ftw.base + namlen + 2;
+    if (data->dirbufsize < new_buflen)
+    {
+        /* Enlarge the buffer.  */
+        char *newp;
+
+        data->dirbufsize = 2 * new_buflen;
+        newp = (char *) realloc (data->dirbuf, data->dirbufsize);
+        if (newp == NULL)
+            return -1;
+        data->dirbuf = newp;
+    }
+
+    *((char *) __mempcpy (data->dirbuf + data->ftw.base, name, namlen)) = '\0';
+
+    int statres;
+    if (dir->streamfd != -1)
+        statres = FXSTATAT (_STAT_VER, dir->streamfd, name, &st,
+                            (data->flags & FTW_PHYS) ? AT_SYMLINK_NOFOLLOW : 0);
+    else
+    {
+        if ((data->flags & FTW_CHDIR) == 0)
+            name = data->dirbuf;
+
+        statres = ((data->flags & FTW_PHYS)
+                   ? LXSTAT (_STAT_VER, name, &st)
+                   : XSTAT (_STAT_VER, name, &st));
+    }
+
+    if (statres < 0)
+    {
+        if (errno != EACCES && errno != ENOENT)
+            result = -1;
+        else if (data->flags & FTW_PHYS)
+            flag = FTW_NS;
+        else
+        {
+            if (dir->streamfd != -1)
+                statres = FXSTATAT (_STAT_VER, dir->streamfd, name, &st,
+                                    AT_SYMLINK_NOFOLLOW);
+            else
+                statres = LXSTAT (_STAT_VER, name, &st);
+            if (statres == 0 && S_ISLNK (st.st_mode))
+                flag = FTW_SLN;
+            else
+                flag = FTW_NS;
+        }
+    }
+    else
+    {
+        if (S_ISDIR (st.st_mode))
+            flag = FTW_D;
+        else if (S_ISLNK (st.st_mode))
+            flag = FTW_SL;
+        else
+            flag = FTW_F;
+    }
+
+    if (result == 0
+        && (flag == FTW_NS
+            || !(data->flags & FTW_MOUNT) || st.st_dev == data->dev))
+    {
+        if (flag == FTW_D)
+        {
+            if ((data->flags & FTW_PHYS)
+                || (!find_object (data, &st)
+                    /* Remember the object.  */
+                    && (result = add_object (data, &st)) == 0))
+                result = ftw_dir (data, &st, dir);
+        }
+        else
+            result = (*data->func) (data->dirbuf, &st, data->cvt_arr[flag],
+                                    &data->ftw);
+    }
+
+    if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SUBTREE)
+        result = 0;
+
+    return result;
+}
+
+
+static int
+ftw_dir (struct ftw_data *data, struct STAT *st, struct dir_data *old_dir)
+{
+    struct dir_data dir;
+    struct dirent64 *d;
+    int previous_base = data->ftw.base;
+    int result;
+    char *startp;
+
+    /* Open the stream for this directory.  This might require that
+       another stream has to be closed.  */
+    result = open_dir_stream (old_dir == NULL ? NULL : &old_dir->streamfd,
+                              data, &dir);
+    if (result != 0)
+    {
+        if (errno == EACCES)
+            /* We cannot read the directory.  Signal this with a special flag.  */
+            result = (*data->func) (data->dirbuf, st, FTW_DNR, &data->ftw);
+
+        return result;
+    }
+
+    /* First, report the directory (if not depth-first).  */
+    if (!(data->flags & FTW_DEPTH))
+    {
+        result = (*data->func) (data->dirbuf, st, FTW_D, &data->ftw);
+        if (result != 0)
+        {
+            int save_err;
+        fail:
+            save_err = errno;
+            __closedir (dir.stream);
+            dir.streamfd = -1;
+            __set_errno (save_err);
+
+            if (data->actdir-- == 0)
+                data->actdir = data->maxdir - 1;
+            data->dirstreams[data->actdir] = NULL;
+            return result;
+        }
+    }
+
+    /* If necessary, change to this directory.  */
+    if (data->flags & FTW_CHDIR)
+    {
+        if (__fchdir (dirfd (dir.stream)) < 0)
+        {
+            result = -1;
+            goto fail;
+        }
+    }
+
+    /* Next, update the `struct FTW' information.  */
+    ++data->ftw.level;
+    startp = data->dirbuf + strlen(data->dirbuf);
+    /* There always must be a directory name.  */
+    assert (startp != data->dirbuf);
+    if (startp[-1] != '/')
+        *startp++ = '/';
+    data->ftw.base = startp - data->dirbuf;
+
+    while (dir.stream != NULL && (d = __readdir64 (dir.stream)) != NULL)
+    {
+        result = process_entry (data, &dir, d->d_name, NAMLEN (d));
+        if (result != 0)
+            break;
+    }
+
+    if (dir.stream != NULL)
+    {
+        /* The stream is still open.  I.e., we did not need more
+           descriptors.  Simply close the stream now.  */
+        int save_err = errno;
+
+        assert (dir.content == NULL);
+
+        __closedir (dir.stream);
+        dir.streamfd = -1;
+        __set_errno (save_err);
+
+        if (data->actdir-- == 0)
+            data->actdir = data->maxdir - 1;
+        data->dirstreams[data->actdir] = NULL;
+    }
+    else
+    {
+        int save_err;
+        char *runp = dir.content;
+
+        while (result == 0 && *runp != '\0')
+        {
+            char *endp = strchr (runp, '\0');
+
+            // XXX Should store the d_type values as well?!
+            result = process_entry (data, &dir, runp, endp - runp);
+
+            runp = endp + 1;
+        }
+
+        save_err = errno;
+        free (dir.content);
+        __set_errno (save_err);
+    }
+
+    if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SIBLINGS)
+        result = 0;
+
+    /* Prepare the return, revert the `struct FTW' information.  */
+    data->dirbuf[data->ftw.base - 1] = '\0';
+    --data->ftw.level;
+    if (upfunc)
+        (*upfunc)();
+    data->ftw.base = previous_base;
+
+    /* Finally, if we process depth-first report the directory.  */
+    if (result == 0 && (data->flags & FTW_DEPTH))
+        result = (*data->func) (data->dirbuf, st, FTW_DP, &data->ftw);
+
+    if (old_dir
+        && (data->flags & FTW_CHDIR)
+        && (result == 0
+            || ((data->flags & FTW_ACTIONRETVAL)
+                && (result != -1 && result != FTW_STOP))))
+    {
+        /* Change back to the parent directory.  */
+        int done = 0;
+        if (old_dir->stream != NULL)
+            if (__fchdir (dirfd (old_dir->stream)) == 0)
+                done = 1;
+
+        if (!done)
+        {
+            if (data->ftw.base == 1)
+            {
+                if (__chdir ("/") < 0)
+                    result = -1;
+            }
+            else
+                if (__chdir ("..") < 0)
+                    result = -1;
+        }
+    }
+
+    return result;
+}
+
+
+static int ftw_startup (const char *dir,
+                        int is_nftw,
+                        void *func,
+                        dir_notification_func_t up,
+                        int descriptors,
+                        int flags)
+{
+    struct ftw_data data;
+    struct STAT st;
+    int result = 0;
+    int save_err;
+    int cwdfd = -1;
+    char *cwd = NULL;
+    char *cp;
+
+    upfunc = up;
+
+    /* First make sure the parameters are reasonable.  */
+    if (dir[0] == '\0')
+    {
+        __set_errno (ENOENT);
+        return -1;
+    }
+
+    data.maxdir = descriptors < 1 ? 1 : descriptors;
+    data.actdir = 0;
+    data.dirstreams = (struct dir_data **) alloca (data.maxdir
+                                                   * sizeof (struct dir_data *));
+    memset (data.dirstreams, '\0', data.maxdir * sizeof (struct dir_data *));
+
+    /* PATH_MAX is always defined when we get here.  */
+    data.dirbufsize = MAX (2 * strlen (dir), PATH_MAX);
+    data.dirbuf = (char *) malloc (data.dirbufsize);
+    if (data.dirbuf == NULL)
+        return -1;
+    cp = mystpcpy (data.dirbuf, dir);
+    /* Strip trailing slashes.  */
+    while (cp > data.dirbuf + 1 && cp[-1] == '/')
+        --cp;
+    *cp = '\0';
+
+    data.ftw.level = 0;
+
+    /* Find basename.  */
+    while (cp > data.dirbuf && cp[-1] != '/')
+        --cp;
+    data.ftw.base = cp - data.dirbuf;
+
+    data.flags = flags;
+
+    /* This assignment might seem to be strange but it is what we want.
+       The trick is that the first three arguments to the `ftw' and
+       `nftw' callback functions are equal.  Therefore we can call in
+       every case the callback using the format of the `nftw' version
+       and get the correct result since the stack layout for a function
+       call in C allows this.  */
+    data.func = (NFTW_FUNC_T) func;
+
+    /* Since we internally use the complete set of FTW_* values we need
+       to reduce the value range before calling a `ftw' callback.  */
+    data.cvt_arr = is_nftw ? nftw_arr : ftw_arr;
+
+    /* No object known so far.  */
+    data.known_objects = NULL;
+
+    /* Now go to the directory containing the initial file/directory.  */
+    if (flags & FTW_CHDIR)
+    {
+        /* We have to be able to go back to the current working
+           directory.  The best way to do this is to use a file
+           descriptor.  */
+        cwdfd = open (".", O_RDONLY);
+        if (cwdfd == -1)
+        {
+            /* Try getting the directory name.  This can be needed if
+               the current directory is executable but not readable.  */
+            if (errno == EACCES)
+                /* GNU extension ahead.  */
+                cwd = __getcwd(NULL, 0);
+
+            if (cwd == NULL)
+                goto out_fail;
+        }
+        else if (data.maxdir > 1)
+            /* Account for the file descriptor we use here.  */
+            --data.maxdir;
+
+        if (data.ftw.base > 0)
+        {
+            /* Change to the directory the file is in.  In data.dirbuf
+               we have a writable copy of the file name.  Just NUL
+               terminate it for now and change the directory.  */
+            if (data.ftw.base == 1)
+                /* I.e., the file is in the root directory.  */
+                result = __chdir ("/");
+            else
+            {
+                char ch = data.dirbuf[data.ftw.base - 1];
+                data.dirbuf[data.ftw.base - 1] = '\0';
+                result = __chdir (data.dirbuf);
+                data.dirbuf[data.ftw.base - 1] = ch;
+            }
+        }
+    }
+
+    /* Get stat info for start directory.  */
+    if (result == 0)
+    {
+        const char *name;
+
+        if (data.flags & FTW_CHDIR)
+        {
+            name = data.dirbuf + data.ftw.base;
+            if (name[0] == '\0')
+                name = ".";
+        }
+        else
+            name = data.dirbuf;
+
+        if (((flags & FTW_PHYS)
+             ? LXSTAT (_STAT_VER, name, &st)
+             : XSTAT (_STAT_VER, name, &st)) < 0)
+        {
+            if (!(flags & FTW_PHYS)
+                && errno == ENOENT
+                && LXSTAT (_STAT_VER, name, &st) == 0
+                && S_ISLNK (st.st_mode))
+                result = (*data.func) (data.dirbuf, &st, data.cvt_arr[FTW_SLN],
+                                       &data.ftw);
+            else
+                /* No need to call the callback since we cannot say anything
+                   about the object.  */
+                result = -1;
+        }
+        else
+        {
+            if (S_ISDIR (st.st_mode))
+            {
+                /* Remember the device of the initial directory in case
+                   FTW_MOUNT is given.  */
+                data.dev = st.st_dev;
+
+                /* We know this directory now.  */
+                if (!(flags & FTW_PHYS))
+                    result = add_object (&data, &st);
+
+                if (result == 0)
+                    result = ftw_dir (&data, &st, NULL);
+            }
+            else
+            {
+                int flag = S_ISLNK (st.st_mode) ? FTW_SL : FTW_F;
+
+                result = (*data.func) (data.dirbuf, &st, data.cvt_arr[flag],
+                                       &data.ftw);
+            }
+        }
+
+        if ((flags & FTW_ACTIONRETVAL)
+            && (result == FTW_SKIP_SUBTREE || result == FTW_SKIP_SIBLINGS))
+            result = 0;
+    }
+
+    /* Return to the start directory (if necessary).  */
+    if (cwdfd != -1)
+    {
+        int save_err = errno;
+        __fchdir (cwdfd);
+        close(cwdfd);
+        __set_errno (save_err);
+    }
+    else if (cwd != NULL)
+    {
+        int save_err = errno;
+        __chdir (cwd);
+        free (cwd);
+        __set_errno (save_err);
+    }
+
+    /* Free all memory.  */
+out_fail:
+    save_err = errno;
+    mytdestroy (data.known_objects, free);
+    free (data.dirbuf);
+    __set_errno (save_err);
+
+    return result;
+}
+
+
+
+/* Entry points.  */
+int NFTW_NAME(const char *path,
+              NFTW_FUNC_T func,
+              dir_notification_func_t up,
+              int descriptors,
+              int flags)
+{
+    return ftw_startup (path, 1, func, up, descriptors, flags);
+}
+
index ecaced072f2f397e37acd76e5fcc4c619ec74829..eb98f45b290d5817461b90498a399bfb5034bbf0 100644 (file)
@@ -91,6 +91,19 @@ UAM_MODULE_EXPORT logtype_conf_t type_configs[logtype_end_of_list_marker] = {
     DEFAULT_LOG_CONFIG /* logtype_uams */
 };
 
+/* We use this in order to track the last n log messages in order to prevent flooding */
+#define LOG_FLOODING_MINCOUNT 5 /* this controls after how many consecutive messages must be detected
+                                   before we start to hide them */
+#define LOG_FLOODING_MAXCOUNT 1000 /* this controls after how many consecutive messages we force a 
+                                      "repeated x times" message */
+#define LOG_FLOODING_ARRAY_SIZE 3 /* this contols how many messages in flow we track */
+struct log_flood_entry {
+    int count;
+    unsigned int hash;
+};
+static struct log_flood_entry log_flood_array[LOG_FLOODING_ARRAY_SIZE];
+static int log_flood_entries;
+
 /* These are used by the LOG macro to store __FILE__ and __LINE__ */
 static const char *log_src_filename;
 static int  log_src_linenumber;
@@ -110,6 +123,20 @@ static const unsigned int num_loglevel_strings = COUNT_ARRAY(arr_loglevel_string
    Internal function definitions
    ========================================================================= */
 
+/* Hash a log message */
+static unsigned int hash_message(const char *message)
+{
+    const char *p = message;
+    unsigned int hash = 0, i = 7;
+
+    while (*p) {
+        hash += *p * i;
+        i++;
+        p++;
+    }
+    return hash;
+}
+
 /*
  * If filename == NULL its for syslog logging, otherwise its for file-logging.
  * "unsetuplog" calls with loglevel == NULL.
@@ -493,7 +520,7 @@ void make_log_entry(enum loglevels loglevel, enum logtypes logtype,
 
     if (fd < 0) {
         /* no where to send the output, give up */
-        return;
+        goto exit;
     }
 
     /* Initialise the Messages */
@@ -512,6 +539,66 @@ void make_log_entry(enum loglevels loglevel, enum logtypes logtype,
         temp_buffer[len+1] = 0;
     }
 
+    if (type_configs[logtype].level >= log_debug)
+        goto log; /* bypass flooding checks */
+
+    /* Prevent flooding: hash the message and check if we got the same one recently */
+    int hash = hash_message(temp_buffer) + log_src_linenumber;
+
+    /* Search for the same message by hash */
+    for (int i = log_flood_entries - 1; i >= 0; i--) {
+        if (log_flood_array[i].hash == hash) {
+
+            /* found same message */
+            log_flood_array[i].count++;
+
+            /* Check if that message has reached LOG_FLOODING_MAXCOUNT */
+            if (log_flood_array[i].count >= LOG_FLOODING_MAXCOUNT) {
+                /* yes, log it and remove from array */
+
+                /* reusing log_details_buffer */
+                sprintf(log_details_buffer, "message repeated %i times\n",
+                        LOG_FLOODING_MAXCOUNT - 1);
+                write(fd, log_details_buffer, strlen(log_details_buffer));
+
+                if ((i + 1) == LOG_FLOODING_ARRAY_SIZE) {
+                    /* last array element, just decrement count */
+                    log_flood_entries--;
+                    goto exit;
+                }
+                /* move array elements down */
+                for (int j = i + 1; j != LOG_FLOODING_ARRAY_SIZE ; j++)
+                    log_flood_array[j-1] = log_flood_array[j];
+                log_flood_entries--;
+            }
+
+            if (log_flood_array[i].count < LOG_FLOODING_MINCOUNT)
+                /* log it */
+                goto log;
+            /* discard it */
+            goto exit;
+        } /* if */
+    }  /* for */
+
+    /* No matching message found, add this message to array*/
+    if (log_flood_entries == LOG_FLOODING_ARRAY_SIZE) {
+        /* array is full, discard oldest entry printing "message repeated..." if count > 1 */
+        if (log_flood_array[0].count >= LOG_FLOODING_MINCOUNT) {
+            /* reusing log_details_buffer */
+            sprintf(log_details_buffer, "message repeated %i times\n",
+                    log_flood_array[0].count - LOG_FLOODING_MINCOUNT + 1);
+            write(fd, log_details_buffer, strlen(log_details_buffer));
+        }
+        for (int i = 1; i < LOG_FLOODING_ARRAY_SIZE; i++) {
+            log_flood_array[i-1] = log_flood_array[i];
+        }
+        log_flood_entries--;
+    }
+    log_flood_array[log_flood_entries].count = 1;
+    log_flood_array[log_flood_entries].hash = hash;
+    log_flood_entries++;
+
+log:
     if ( ! log_config.console) {
         generate_message_details(log_details_buffer, sizeof(log_details_buffer),
                                  type_configs[logtype].set ?
@@ -529,6 +616,7 @@ void make_log_entry(enum loglevels loglevel, enum logtypes logtype,
         write(fd, temp_buffer, strlen(temp_buffer));
     }
 
+exit:
     inlog = 0;
 }
 
diff --git a/libatalk/util/queue.c b/libatalk/util/queue.c
new file mode 100644 (file)
index 0000000..114e640
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+  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.
+*/
+
+/*!
+ * @file
+ * Netatalk utility functions: queue
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <stdlib.h>
+
+#include <atalk/queue.h>
+
+static qnode_t *alloc_init_node(void *data)
+{
+    qnode_t *node;
+    if ((node = malloc(sizeof(qnode_t))) == NULL)
+        return NULL;
+    node->data = data;
+
+    return node;
+}
+
+/********************************************************************************
+ * Interface
+ *******************************************************************************/
+
+q_t *queue_init(void)
+{
+    q_t *queue;
+
+    if ((queue = alloc_init_node(NULL)) == NULL)
+        return NULL;
+
+    queue->prev = queue->next = queue;
+    return queue;
+}
+
+/* Insert at tail */
+qnode_t *enqueue(q_t *q, void *data)
+{
+    qnode_t *node;
+
+    if ((node = alloc_init_node(data)) == NULL)
+        return NULL;
+
+    /* insert at tail */
+    node->next = q;
+    node->prev = q->prev;
+    q->prev->next = node;
+    q->prev = node;
+
+    return node;
+}
+
+/* Insert at head */
+qnode_t *prequeue(q_t *q, void *data)
+{
+    qnode_t *node;
+
+    if ((node = alloc_init_node(data)) == NULL)
+        return NULL;
+
+    /* insert at head */
+    q->next->prev = node;
+    node->next = q->next;
+    node->prev = q;
+    q->next = node;
+
+    return node;
+}
+
+/* Take from head */
+void *dequeue(q_t *q)
+{
+    qnode_t *node;
+    void *data;
+
+    if (q == NULL || q->next == q)
+        return NULL;
+
+    /* take first node from head */
+    node = q->next;
+    data = node->data;
+    q->next = node->next;
+    node->next->prev = node->prev;
+    free(node);
+
+    return data;    
+}
+
+void queue_destroy(q_t *q, void (*callback)(void *))
+{
+    void *p;
+
+    while ((p = dequeue(q)) != NULL)
+        callback(p);
+
+    free(q);
+    q = NULL;
+}
+
index 85c150c4c37cd9290f0aae8a53a99ab606d8660f..ecefbd2cb7502c65ff349e68b3b845f65e88d91b 100644 (file)
@@ -1,15 +1,13 @@
 /*
- * $Id: server_child.c,v 1.12 2010-01-21 14:14:49 didg Exp $
- *
  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
  * All rights reserved. See COPYRIGHT.
  *
  *
- * handle inserting, removing, and freeing of children. 
+ * handle inserting, removing, and freeing of children.
  * this does it via a hash table. it incurs some overhead over
  * a linear append/remove in total removal and kills, but it makes
  * single-entry removals a fast operation. as total removals occur during
- * child initialization and kills during server shutdown, this is 
+ * child initialization and kills during server shutdown, this is
  * probably a win for a lot of connections and unimportant for a small
  * number of connections.
  */
@@ -24,7 +22,7 @@
 #include <unistd.h>
 #endif /* HAVE_UNISTD_H */
 #include <signal.h>
-#include <atalk/logger.h>
+#include <errno.h>
 
 /* POSIX.1 sys/wait.h check */
 #include <sys/types.h>
 #endif /* HAVE_SYS_WAIT_H */
 #include <sys/time.h>
 
+#include <atalk/logger.h>
+#include <atalk/errchk.h>
+#include <atalk/util.h>
+#include <atalk/server_child.h>
+
 #ifndef WEXITSTATUS
 #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
 #endif /* ! WEXITSTATUS */
 #define WIFSTOPPED(status) (((status) & 0xff) == 0x7f)
 #endif
 #ifndef WIFSIGNALED
-#define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status)) 
+#define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status))
 #endif
 #ifndef WTERMSIG
 #define WTERMSIG(status)      ((status) & 0x7f)
 #endif
 
-#include <atalk/server_child.h>
-
 /* hash/child functions: hash OR's pid */
 #define CHILD_HASHSIZE 32
 #define HASH(i) ((((i) >> 8) ^ (i)) & (CHILD_HASHSIZE - 1))
 
-struct server_child_data {
-  pid_t     pid;               /* afpd worker process pid (from the worker afpd process )*/
-  uid_t     uid;               /* user id of connected client (from the worker afpd process) */
-  int       valid;             /* 1 if we have a clientid */
-  u_int32_t time;              /* client boot time (from the mac client) */
-  int       killed;            /* 1 if we already tried to kill the client */
-
-  u_int32_t idlen;             /* clientid len (from the Mac client) */
-  char *clientid;              /* clientid (from the Mac client) */
-  struct server_child_data **prevp, *next;
-};
-
 typedef struct server_child_fork {
-  struct server_child_data *table[CHILD_HASHSIZE];
-  void (*cleanup)(const pid_t);
+    struct server_child_data *table[CHILD_HASHSIZE];
+    void (*cleanup)(const pid_t);
 } server_child_fork;
 
+int parent_or_child; /* 0: parent, 1: child */
+
 static inline void hash_child(struct server_child_data **htable,
-                                 struct server_child_data *child)
+                              struct server_child_data *child)
 {
-  struct server_child_data **table;
+    struct server_child_data **table;
 
-  table = &htable[HASH(child->pid)];
-  if ((child->next = *table) != NULL)
-    (*table)->prevp = &child->next;
-  *table = child;
-  child->prevp = table;
+    table = &htable[HASH(child->pid)];
+    if ((child->next = *table) != NULL)
+        (*table)->prevp = &child->next;
+    *table = child;
+    child->prevp = table;
 }
 
 static inline void unhash_child(struct server_child_data *child)
 {
-  if (child->prevp) {
-    if (child->next)
-      child->next->prevp = child->prevp;
-    *(child->prevp) = child->next;
-  }
+    if (child->prevp) {
+        if (child->next)
+            child->next->prevp = child->prevp;
+        *(child->prevp) = child->next;
+    }
 }
 
-static inline struct server_child_data 
-*resolve_child(struct server_child_data **table, const pid_t pid)
+static struct server_child_data *resolve_child(struct server_child_data **table, pid_t pid)
 {
-  struct server_child_data *child;
+    struct server_child_data *child;
 
-  for (child = table[HASH(pid)]; child; child = child->next) {
-    if (child->pid == pid)
-      break;
-  }
+    for (child = table[HASH(pid)]; child; child = child->next) {
+        if (child->pid == pid)
+            break;
+    }
 
-  return child;
+    return child;
 }
 
 /* initialize server_child structure */
 server_child *server_child_alloc(const int connections, const int nforks)
 {
-  server_child *children;
+    server_child *children;
 
-  children = (server_child *) calloc(1, sizeof(server_child));
-  if (!children)
-    return NULL;
+    children = (server_child *) calloc(1, sizeof(server_child));
+    if (!children)
+        return NULL;
 
-  children->nsessions = connections;
-  children->nforks = nforks;
-  children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
-  
-  if (!children->fork) {
-    free(children);
-    return NULL;
-  } 
+    children->nsessions = connections;
+    children->nforks = nforks;
+    children->fork = (void *) calloc(nforks, sizeof(server_child_fork));
 
-  return children;
+    if (!children->fork) {
+        free(children);
+        return NULL;
+    }
+
+    return children;
 }
 
-/* add a child in. return 0 on success, -1 on serious error, and
- * > 0 for a non-serious error. */
-int server_child_add(server_child *children, const int forkid,
-                    const pid_t pid)
+/*!
+ * add a child
+ * @return pointer to struct server_child_data on success, NULL on error
+ */
+afp_child_t *server_child_add(server_child *children, int forkid, pid_t pid, uint ipc_fds[2])
 {
-  server_child_fork *fork;
-  struct server_child_data *child;
-  sigset_t sig, oldsig;
-
-  /* we need to prevent deletions from occuring before we get a 
-   * chance to add the child in. */
-  sigemptyset(&sig);
-  sigaddset(&sig, SIGCHLD);
-  sigprocmask(SIG_BLOCK, &sig, &oldsig);
-
-  /* it's possible that the child could have already died before the
-   * sigprocmask. we need to check for this. */
-  if (kill(pid, 0) < 0) {
-    sigprocmask(SIG_SETMASK, &oldsig, NULL);
-    return 1;
-  }
-
-  fork = (server_child_fork *) children->fork + forkid;
-
-  /* if we already have an entry. just return. */
-  if (resolve_child(fork->table, pid)) {
-    sigprocmask(SIG_SETMASK, &oldsig, NULL);
-    return 0;
-  }
-
-  if ((child = (struct server_child_data *) 
-       calloc(1, sizeof(struct server_child_data))) == NULL) {
-    sigprocmask(SIG_SETMASK, &oldsig, NULL);
-    return -1;
-  }
-
-  child->pid = pid;
-  child->valid = 0;
-  child->killed = 0;
-  hash_child(fork->table, child);
-  children->count++;
-  sigprocmask(SIG_SETMASK, &oldsig, NULL);
-
-  return 0;
+    server_child_fork *fork;
+    afp_child_t *child = NULL;
+    sigset_t sig, oldsig;
+
+    /* we need to prevent deletions from occuring before we get a
+     * chance to add the child in. */
+    sigemptyset(&sig);
+    sigaddset(&sig, SIGCHLD);
+    pthread_sigmask(SIG_BLOCK, &sig, &oldsig);
+
+    /* it's possible that the child could have already died before the
+     * pthread_sigmask. we need to check for this. */
+    if (kill(pid, 0) < 0)
+        goto exit;
+
+    fork = (server_child_fork *) children->fork + forkid;
+
+    /* if we already have an entry. just return. */
+    if (child = resolve_child(fork->table, pid))
+        goto exit;
+
+    if ((child = calloc(1, sizeof(afp_child_t))) == NULL)
+        goto exit;
+
+    child->pid = pid;
+    child->valid = 0;
+    child->killed = 0;
+    child->ipc_fds[0] = ipc_fds[0];
+    child->ipc_fds[1] = ipc_fds[1];
+
+    hash_child(fork->table, child);
+    children->count++;
+
+exit:
+    pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
+    return child;
 }
 
 /* remove a child and free it */
-int server_child_remove(server_child *children, const int forkid,
-                       const pid_t pid)
+int server_child_remove(server_child *children, const int forkid, pid_t pid)
 {
-  server_child_fork *fork;
-  struct server_child_data *child;
-
-  fork = (server_child_fork *) children->fork + forkid;
-  if (!(child = resolve_child(fork->table, pid)))
-    return 0;
-  
-  unhash_child(child);
-  if (child->clientid) {
-      free(child->clientid);
-  }
-  free(child);
-  children->count--;
-  return 1;
+    int fd;
+    server_child_fork *fork;
+    struct server_child_data *child;
+
+    fork = (server_child_fork *) children->fork + forkid;
+    if (!(child = resolve_child(fork->table, pid)))
+        return -1;
+
+    unhash_child(child);
+    if (child->clientid) {
+        free(child->clientid);
+        child->clientid = NULL;
+    }
+
+    /* In main:child_handler() we need the fd in order to remove it from the pollfd set */
+    fd = child->ipc_fds[0];
+    if (child->ipc_fds[0] != -1) {
+        close(child->ipc_fds[0]);
+        child->ipc_fds[0] = -1;
+    }
+    if (child->ipc_fds[1] != -1) {
+        close(child->ipc_fds[1]);
+        child->ipc_fds[1] = -1;
+    }
+
+    free(child);
+    children->count--;
+
+    if (fork->cleanup)
+        fork->cleanup(pid);
+
+    return fd;
 }
 
 /* free everything: by using a hash table, this increases the cost of
  * this part over a linked list by the size of the hash table */
 void server_child_free(server_child *children)
 {
-  server_child_fork *fork;
-  struct server_child_data *child, *tmp;
-  int i, j;
-
-  for (i = 0; i < children->nforks; i++) {
-    fork = (server_child_fork *) children->fork + i;
-    for (j = 0; j < CHILD_HASHSIZE; j++) {
-      child = fork->table[j]; /* start at the beginning */
-      while (child) {
-       tmp = child->next;
-        if (child->clientid) {
-            free(child->clientid);
+    server_child_fork *fork;
+    struct server_child_data *child, *tmp;
+    int i, j;
+
+    for (i = 0; i < children->nforks; i++) {
+        fork = (server_child_fork *) children->fork + i;
+        for (j = 0; j < CHILD_HASHSIZE; j++) {
+            child = fork->table[j]; /* start at the beginning */
+            while (child) {
+                tmp = child->next;
+                if (child->clientid) {
+                    free(child->clientid);
+                }
+                free(child);
+                child = tmp;
+            }
         }
-       free(child);
-       child = tmp;
-      }
     }
-  }
-  free(children->fork);
-  free(children);
+    free(children->fork);
+    free(children);
 }
 
-/* send kill to child processes: this also has an increased cost over
- * a plain-old linked list */
-void server_child_kill(server_child *children, const int forkid,
-                      const int sig)
+/* send signal to all child processes */
+void server_child_kill(server_child *children, int forkid, int sig)
 {
-  server_child_fork *fork;
-  struct server_child_data *child, *tmp;
-  int i;
-
-  fork = (server_child_fork *) children->fork + forkid;
-  for (i = 0; i < CHILD_HASHSIZE; i++) {
-    child = fork->table[i];
-    while (child) {
-      tmp = child->next;
-      kill(child->pid, sig);
-      child = tmp;
+    server_child_fork *fork;
+    struct server_child_data *child, *tmp;
+    int i;
+
+    fork = (server_child_fork *) children->fork + forkid;
+    for (i = 0; i < CHILD_HASHSIZE; i++) {
+        child = fork->table[i];
+        while (child) {
+            tmp = child->next;
+            kill(child->pid, sig);
+            child = tmp;
+        }
     }
-  }
 }
 
 /* send kill to a child processes.
- * a plain-old linked list 
+ * a plain-old linked list
  * FIXME use resolve_child ?
  */
 static int kill_child(struct server_child_data *child)
 {
-  if (!child->killed) {
-     kill(child->pid, SIGTERM);
-     /* we don't wait because there's no guarantee that we can really kill it */
-     child->killed = 1;
-     return 1;
-  }
-  else {
-     LOG(log_info, logtype_default, "Already tried to kill (%d) before! Still there?",  child->pid);
-  }
-  return 0;
+    if (!child->killed) {
+        kill(child->pid, SIGTERM);
+        /* we don't wait because there's no guarantee that we can really kill it */
+        child->killed = 1;
+        return 1;
+    } else {
+        LOG(log_info, logtype_default, "Unresponsive child[%d], sending SIGKILL", child->pid);
+        kill(child->pid, SIGKILL);
+    }
+    return 1;
 }
 
-/* -------------------- */
-void server_child_kill_one(server_child *children, const int forkid, const pid_t pid, const uid_t uid)
+/*!
+ * Try to find an old session and pass socket
+ * @returns -1 on error, 0 if no matching session was found, 1 if session was found and socket passed
+ */
+int server_child_transfer_session(server_child *children,
+                                  int forkid,
+                                  pid_t pid,
+                                  uid_t uid,
+                                  int afp_socket,
+                                  uint16_t DSI_requestID)
 {
-  server_child_fork *fork;
-  struct server_child_data *child, *tmp;
-  int i;
-
-  fork = (server_child_fork *) children->fork + forkid;
-  for (i = 0; i < CHILD_HASHSIZE; i++) {
-    child = fork->table[i];
-    while (child) {
-      tmp = child->next;
-      if (child->pid == pid) {
-          if (!child->valid) {
-             /* hmm, client 'guess' the pid, rogue? */
-             LOG(log_info, logtype_default, "Disconnecting old session (%d) no token, bailout!",  child->pid);
-          }
-          else if (child->uid != uid) {
-             LOG(log_info, logtype_default, "Disconnecting old session (%d) not the same user, bailout!",  child->pid);
-          }
-          else {
-              kill_child(child);
-          }
-      }
-      child = tmp;
+    EC_INIT;
+    server_child_fork *fork;
+    struct server_child_data *child;
+    int i;
+
+    fork = (server_child_fork *) children->fork + forkid;
+    if ((child = resolve_child(fork->table, pid)) == NULL) {
+        LOG(log_note, logtype_default, "Reconnect: no child[%u]", pid);
+        return 0;
+    }
+
+    if (!child->valid) {
+        /* hmm, client 'guess' the pid, rogue? */
+        LOG(log_error, logtype_default, "Reconnect: invalidated child[%u]", pid);
+        return 0;
+    } else if (child->uid != uid) {
+        LOG(log_error, logtype_default, "Reconnect: child[%u] not the same user", pid);
+        return 0;
     }
-  }
+
+    LOG(log_note, logtype_default, "Reconnect: transfering session to child[%u]", pid);
+    
+    if (writet(child->ipc_fds[0], &DSI_requestID, 2, 0, 2) != 2) {
+        LOG(log_error, logtype_default, "Reconnect: error sending DSI id to child[%u]", pid);
+        EC_STATUS(-1);
+        goto EC_CLEANUP;
+    }
+    EC_ZERO_LOG(send_fd(child->ipc_fds[0], afp_socket));
+    EC_ZERO_LOG(kill(pid, SIGURG));
+
+    EC_STATUS(1);
+
+EC_CLEANUP:
+    EC_EXIT;
 }
 
 
 /* see if there is a process for the same mac     */
 /* if the times don't match mac has been rebooted */
-void server_child_kill_one_by_id(server_child *children, const int forkid, const pid_t pid,
-          const uid_t uid, 
-          const u_int32_t idlen, char *id, u_int32_t boottime)
+void server_child_kill_one_by_id(server_child *children, int forkid, pid_t pid,
+                                 uid_t uid, uint32_t idlen, char *id, uint32_t boottime)
 {
-  server_child_fork *fork;
-  struct server_child_data *child, *tmp;
-  int i;
-
-  fork = (server_child_fork *) children->fork + forkid;
-  for (i = 0; i < CHILD_HASHSIZE; i++) {
-    child = fork->table[i];
-    while (child) {
-      tmp = child->next;
-      if ( child->pid != pid) {
-          if ( child->idlen == idlen && !memcmp(child->clientid, id, idlen)) {
-            if ( child->time != boottime ) {
-                 if (uid == child->uid) {
-                     if (kill_child(child)) {
-                         LOG(log_info, logtype_default, "Disconnecting old session %d, client rebooted.",  child->pid);
-                     }
-                 }
-                 else {
-                     LOG(log_info, logtype_default, "Disconnecting old session not the same uid, bailout!");
-                 }
-            }
-            else if (child->killed) {
-                 /* there's case where a Mac close a connection and restart a new one before
-                  * the first is 'waited' by the master afpd process
-                 */
-                 LOG(log_info, logtype_default, 
-                     "WARNING: connection (%d) killed but still there.", child->pid);
-            }
-            else {
-                 LOG(log_info, logtype_default, 
-                     "WARNING: 2 connections (%d, %d), boottime identical, don't know if one needs to be disconnected.",
-                      child->pid, pid);
-            } 
-               
-         }
-      }
-      else 
-      {
-         child->time = boottime;
-         /* free old token if any */
-         if (child->clientid) {
-             free(child->clientid);
-         }
-          child->uid = uid; 
-          child->valid = 1;
-         child->idlen = idlen;
-          child->clientid = id;
-         LOG(log_debug, logtype_default, "Setting clientid (len %d) for %d, boottime %X", idlen, child->pid, boottime);
-      }
-      child = tmp;
+    server_child_fork *fork;
+    struct server_child_data *child, *tmp;
+    int i;
+
+    fork = (server_child_fork *)children->fork + forkid;
+
+    for (i = 0; i < CHILD_HASHSIZE; i++) {
+        child = fork->table[i];
+        while (child) {
+            tmp = child->next;
+            if ( child->pid != pid) {
+                if (child->idlen == idlen && memcmp(child->clientid, id, idlen) == 0) {
+                    if ( child->time != boottime ) {
+                        /* Client rebooted */
+                        if (uid == child->uid) {
+                            kill_child(child);
+                            LOG(log_warning, logtype_default,
+                                "Terminated disconnected child[%u], client rebooted.",
+                                child->pid);
+                        } else {
+                            LOG(log_warning, logtype_default,
+                                "Session with different pid[%u]", child->pid);
+                        }
+                    } else {
+                        /* One client with multiple sessions */
+                        LOG(log_debug, logtype_default,
+                            "Found another session[%u] for client[%u]", child->pid, pid);
+                    }
+                }
+            } else {
+                /* update childs own slot */
+                child->time = boottime;
+                if (child->clientid)
+                    free(child->clientid);
+                LOG(log_debug, logtype_default, "Setting client ID for %u", child->pid);
+                child->uid = uid;
+                child->valid = 1;
+                child->idlen = idlen;
+                child->clientid = id;
+            }
+            child = tmp;
+        }
     }
-  }
 }
 
 /* for extra cleanup if necessary */
 void server_child_setup(server_child *children, const int forkid,
-                         void (*fcn)(const pid_t))
+                        void (*fcn)(const pid_t))
 {
-  server_child_fork *fork;
+    server_child_fork *fork;
 
-  fork = (server_child_fork *) children->fork + forkid;
-  fork->cleanup = fcn;
+    fork = (server_child_fork *) children->fork + forkid;
+    fork->cleanup = fcn;
 }
 
 
-/* keep track of children. */
-void server_child_handler(server_child *children)
-{
-  int status, i;
-  pid_t pid;
-  
-#ifndef WAIT_ANY
-#define WAIT_ANY (-1)
-#endif /* ! WAIT_ANY */
-
-  while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
-    for (i = 0; i < children->nforks; i++) {
-      if (server_child_remove(children, i, pid)) {
-        server_child_fork *fork;
-        
-       fork = (server_child_fork *) children->fork + i;
-       if (fork->cleanup)
-         fork->cleanup(pid);
-       break;
-      }
-    }
-
-    if (WIFEXITED(status)) {
-      if (WEXITSTATUS(status)) {
-       LOG(log_info, logtype_default, "server_child[%d] %d exited %d", i, pid, 
-              WEXITSTATUS(status));
-      } else {
-       LOG(log_info, logtype_default, "server_child[%d] %d done", i, pid);
-      }
-    } else {
-      if (WIFSIGNALED(status))
-      { 
-       LOG(log_info, logtype_default, "server_child[%d] %d killed by signal %d", i, pid,  
-              WTERMSIG (status));
-      }
-      else
-      {
-       LOG(log_info, logtype_default, "server_child[%d] %d died", i, pid);
-      }
-    }
-  }
-}
-
-/* --------------------------- 
+/* ---------------------------
  * reset children signals
-*/
+ */
 void server_reset_signal(void)
 {
     struct sigaction    sv;
@@ -415,18 +382,18 @@ void server_reset_signal(void)
     memset(&sv, 0, sizeof(sv));
     sv.sa_handler =  SIG_DFL;
     sigemptyset( &sv.sa_mask );
-    
+
     sigaction(SIGALRM, &sv, NULL );
     sigaction(SIGHUP,  &sv, NULL );
     sigaction(SIGTERM, &sv, NULL );
     sigaction(SIGUSR1, &sv, NULL );
     sigaction(SIGCHLD, &sv, NULL );
-    
+
     sigemptyset(&sigs);
     sigaddset(&sigs, SIGALRM);
     sigaddset(&sigs, SIGHUP);
     sigaddset(&sigs, SIGUSR1);
     sigaddset(&sigs, SIGCHLD);
-    sigprocmask(SIG_UNBLOCK, &sigs, NULL);
-        
+    pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
+
 }
index 289197fb7ac89a76c2da7e0cd91a3abfa2527e96..1115551ee4e10826f56aaa1d8fd2b807ecb25371 100644 (file)
@@ -1,10 +1,7 @@
 /*
- * $Id: server_ipc.c,v 1.4 2010-01-21 14:14:49 didg Exp $
- *
  * All rights reserved. See COPYRIGHT.
  *
- *
- * ipc between parent and children.
+ * IPC over socketpair between parent and children.
  */
 
 #ifdef HAVE_CONFIG_H
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <signal.h>
 
 #include <atalk/server_child.h>
 #include <atalk/server_ipc.h>
 #include <atalk/logger.h>
+#include <atalk/util.h>
 
 typedef struct ipc_header {
-       u_int16_t command;
-        pid_t    child_pid;
-        uid_t     uid;
-        u_int32_t len;
-       char      *msg;
-} ipc_header;
-
-static int pipe_fd[2];
-   
-void *server_ipc_create(void)
-{
-    if (pipe(pipe_fd)) {
-        return NULL;
-    }
-    return &pipe_fd;
-}
-
-/* ----------------- */
-int server_ipc_child(void *obj _U_)
-{
-    /* close input */
-    close(pipe_fd[0]);
-    return pipe_fd[1];
-}
+       uint16_t command;
+    pid_t       child_pid;
+    uid_t    uid;
+    uint32_t len;
+       char     *msg;
+    int      afp_socket;
+    uint16_t DSI_requestID;
+} ipc_header_t;
+
+static char *ipc_cmd_str[] = { "IPC_DISCOLDSESSION",
+                               "IPC_GETSESSION"};
 
-/* ----------------- */
-int server_ipc_parent(void *obj _U_)
-{
-    return pipe_fd[0];
-}
-
-/* ----------------- */
-static int ipc_kill_token (struct ipc_header *ipc, server_child *children)
+/*
+ * Pass afp_socket to old disconnected session if one has a matching token (token = pid)
+ * @returns -1 on error, 0 if no matching session was found, 1 if session was found and socket passed
+ */
+static int ipc_kill_token(struct ipc_header *ipc, server_child *children)
 {
     pid_t pid;
 
@@ -66,22 +51,25 @@ static int ipc_kill_token (struct ipc_header *ipc, server_child *children)
     /* assume signals SA_RESTART set */
     memcpy (&pid, ipc->msg, sizeof(pid_t));
 
-    LOG(log_info, logtype_default, "child %d user %d disconnected", pid, ipc->uid);
-    server_child_kill_one(children, CHILD_DSIFORK, pid, ipc->uid);
-    return 0;
+    return server_child_transfer_session(children,
+                                         CHILD_DSIFORK,
+                                         pid,
+                                         ipc->uid,
+                                         ipc->afp_socket,
+                                         ipc->DSI_requestID);
 }
 
 /* ----------------- */
-static int ipc_get_session (struct ipc_header *ipc, server_child *children)
+static int ipc_get_session(struct ipc_header *ipc, server_child *children)
 {
     u_int32_t boottime;
     u_int32_t idlen;
     char     *clientid, *p;
 
+    
+    if (ipc->len < (sizeof(idlen) + sizeof(boottime)) )
+        return -1;
 
-    if (ipc->len < (sizeof(idlen) + sizeof(boottime)) ) {
-       return -1;
-    }
     p = ipc->msg;
     memcpy (&idlen, p, sizeof(idlen));
     idlen = ntohl (idlen);
@@ -90,17 +78,24 @@ static int ipc_get_session (struct ipc_header *ipc, server_child *children)
     memcpy (&boottime, p, sizeof(boottime));
     p += sizeof(boottime);
     
-    if (ipc->len < idlen + sizeof(idlen) + sizeof(boottime)) {
-       return -1;
-    }
-    if (NULL == (clientid = (char*) malloc(idlen)) ) {
-       return -1;
-    }
+    if (ipc->len < idlen + sizeof(idlen) + sizeof(boottime))
+        return -1;
+
+    if (NULL == (clientid = (char*) malloc(idlen)) )
+        return -1;
     memcpy (clientid, p, idlen);
   
-    server_child_kill_one_by_id (children, CHILD_DSIFORK, ipc->child_pid, ipc->uid, idlen, clientid, boottime);
-    /* FIXME byte to ascii if we want to log clientid */
-    LOG (log_debug, logtype_afpd, "ipc_get_session: len: %u, idlen %d, time %x", ipc->len, idlen, boottime); 
+    LOG(log_debug, logtype_afpd, "ipc_get_session(pid: %u, uid: %u, time: 0x%08x)",
+        ipc->child_pid, ipc->uid, boottime); 
+
+    server_child_kill_one_by_id(children,
+                                CHILD_DSIFORK,
+                                ipc->child_pid,
+                                ipc->uid,
+                                idlen,
+                                clientid,
+                                boottime);
+
     return 0;
 }
 
@@ -114,16 +109,26 @@ static int ipc_get_session (struct ipc_header *ipc, server_child *children)
  * uid
  * 
 */
-int server_ipc_read(server_child *children)
+
+/*!
+ * Read a IPC message from a child
+ *
+ * @args children  (rw) pointer to our structure with all childs
+ * @args fd        (r)  IPC socket with child
+ *
+ * @returns number of bytes transfered, -1 on error, 0 on EOF
+ */
+int ipc_server_read(server_child *children, int fd)
 {
     int       ret = 0;
     struct ipc_header ipc;
     char      buf[IPC_MAXMSGSIZE], *p;
 
-    if ((ret = read(pipe_fd[0], buf, IPC_HEADERLEN)) != IPC_HEADERLEN) {
-       LOG (log_info, logtype_afpd, "Reading IPC header failed (%u of %u  bytes read)", ret, IPC_HEADERLEN);
-       return -1;
-    } 
+    if ((ret = read(fd, buf, IPC_HEADERLEN)) != IPC_HEADERLEN) {
+        LOG(log_error, logtype_afpd, "Reading IPC header failed (%i of %u bytes read): %s",
+            ret, IPC_HEADERLEN, strerror(errno));
+        return ret;
+    }
 
     p = buf;
 
@@ -139,40 +144,62 @@ int server_ipc_read(server_child *children)
     memcpy(&ipc.len, p, sizeof(ipc.len));
 
     /* This should never happen */
-    if (ipc.len > (IPC_MAXMSGSIZE - IPC_HEADERLEN))
-    {
-       LOG (log_info, logtype_afpd, "IPC message exceeds allowed size (%u)", ipc.len);
-       return -1;
+    if (ipc.len > (IPC_MAXMSGSIZE - IPC_HEADERLEN)) {
+        LOG (log_info, logtype_afpd, "IPC message exceeds allowed size (%u)", ipc.len);
+        return -1;
     }
 
     memset (buf, 0, IPC_MAXMSGSIZE);
     if ( ipc.len != 0) {
-           if ((ret = read(pipe_fd[0], buf, ipc.len)) != (int) ipc.len) {
-               LOG (log_info, logtype_afpd, "Reading IPC message failed (%u of %u  bytes read)", ret, ipc.len);
-               return -1;
+           if ((ret = read(fd, buf, ipc.len)) != (int) ipc.len) {
+            LOG(log_info, logtype_afpd, "Reading IPC message failed (%u of %u  bytes read): %s",
+                ret, ipc.len, strerror(errno));
+            return ret;
        }        
     }
     ipc.msg = buf;
-    
-    LOG (log_debug, logtype_afpd, "ipc_read: command: %u, pid: %u, len: %u", ipc.command, ipc.child_pid, ipc.len); 
 
-    switch (ipc.command)
-    {
-       case IPC_KILLTOKEN:
-               return (ipc_kill_token(&ipc, children));
-               break;
+    LOG(log_debug, logtype_afpd, "ipc_server_read(%s): pid: %u",
+        ipc_cmd_str[ipc.command], ipc.child_pid); 
+
+    int afp_socket;
+
+    switch (ipc.command) {
+
+       case IPC_DISCOLDSESSION:
+        if (readt(fd, &ipc.DSI_requestID, 2, 0, 2) != 2) {
+            LOG (log_error, logtype_afpd, "ipc_read(%s:child[%u]): couldnt read DSI id: %s",
+                 ipc_cmd_str[ipc.command], ipc.child_pid, strerror(errno));
+        }
+        if ((ipc.afp_socket = recv_fd(fd, 1)) == -1) {
+            LOG (log_error, logtype_afpd, "ipc_read(%s:child[%u]): recv_fd: %s",
+                 ipc_cmd_str[ipc.command], ipc.child_pid, strerror(errno));
+            return -1;
+        }
+               if (ipc_kill_token(&ipc, children) == 1) {
+            /* Transfered session (ie afp_socket) to old disconnected child, now kill the new one */
+            LOG(log_note, logtype_afpd, "Reconnect: killing new session child[%u] after transfer",
+                ipc.child_pid);
+            kill(ipc.child_pid, SIGTERM);
+        }        
+        close(ipc.afp_socket);
+        break;
+
        case IPC_GETSESSION:
-               return (ipc_get_session(&ipc, children));
-               break;
+               if (ipc_get_session(&ipc, children) != 0)
+            return -1;
+        break;
+
        default:
                LOG (log_info, logtype_afpd, "ipc_read: unknown command: %d", ipc.command);
                return -1;
     }
 
+    return ret;
 }
 
 /* ----------------- */
-int server_ipc_write( u_int16_t command, int len, void *msg)
+int ipc_child_write(int fd, uint16_t command, int len, void *msg)
 {
    char block[IPC_MAXMSGSIZE], *p;
    pid_t pid;
@@ -204,7 +231,8 @@ int server_ipc_write( u_int16_t command, int len, void *msg)
 
    memcpy(p, msg, len);
 
-   LOG (log_debug, logtype_afpd, "ipc_write: command: %u, pid: %u, msglen: %u", command, pid, len); 
-   return write(pipe_fd[1], block, len+IPC_HEADERLEN );
+   LOG(log_debug, logtype_afpd, "ipc_child_write(%s)", ipc_cmd_str[command]);
+
+   return write(fd, block, len+IPC_HEADERLEN );
 }
 
index af41825f85b3d307b446ba0750120bf5b64c88cf..528b2646bcfe2885bd2c0450b61b4429825f1082 100644 (file)
@@ -1,5 +1,4 @@
 /*
-   $Id: socket.c,v 1.6 2010-01-05 19:05:52 franklahm Exp $
    Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
 
    This program is free software; you can redistribute it and/or modify
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
 
+#ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 600
+#endif
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__
+#endif
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/types.h>
 #include <netinet/in.h>
 #include <stdlib.h>
 #include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <time.h>
+#include <sys/ioctl.h>
+
+#include <atalk/logger.h>
+#include <atalk/util.h>
 
 static char ipv4mapprefix[] = {0,0,0,0,0,0,0,0,0,0,0xff,0xff};
 
@@ -62,6 +77,197 @@ int setnonblock(int fd, int cmd)
     return 0;
 }
 
+/*!
+ * non-blocking drop-in replacement for read with timeout using select
+ *
+ * @param socket          (r)  socket, if in blocking mode, pass "setnonblocking" arg as 1
+ * @param data            (rw) buffer for the read data
+ * @param lenght          (r)  how many bytes to read
+ * @param setnonblocking  (r)  when non-zero this func will enable and disable non blocking
+ *                             io mode for the socket
+ * @param timeout         (r)  number of seconds to try reading
+ *
+ * @returns number of bytes actually read or -1 on timeout or error
+ */
+ssize_t readt(int socket, void *data, const size_t length, int setnonblocking, int timeout)
+{
+    size_t stored = 0;
+    ssize_t len = 0;
+    struct timeval now, end, tv;
+    fd_set rfds;
+    int ret;
+
+    FD_ZERO(&rfds);
+
+    if (setnonblocking) {
+        if (setnonblock(socket, 1) != 0)
+            return -1;
+    }
+
+    /* Calculate end time */
+    (void)gettimeofday(&now, NULL);
+    end = now;
+    end.tv_sec += timeout;
+
+    while (stored < length) {
+        len = read(socket, (char *) data + stored, length - stored);
+        if (len == -1) {
+            switch (errno) {
+            case EINTR:
+                continue;
+            case EAGAIN:
+                FD_SET(socket, &rfds);
+                tv.tv_usec = 0;
+                tv.tv_sec  = timeout;
+                        
+                while ((ret = select(socket + 1, &rfds, NULL, NULL, &tv)) < 1) {
+                    switch (ret) {
+                    case 0:
+                        LOG(log_debug, logtype_afpd, "select timeout %d s", timeout);
+                        errno = EAGAIN;
+                        goto exit;
+
+                    default: /* -1 */
+                        if (errno == EINTR) {
+                            (void)gettimeofday(&now, NULL);
+                            if (now.tv_sec >= end.tv_sec && now.tv_usec >= end.tv_usec) {
+                                LOG(log_warning, logtype_afpd, "select timeout %d s", timeout);
+                                goto exit;
+                            }
+                            if (now.tv_usec > end.tv_usec) {
+                                tv.tv_usec = 1000000 + end.tv_usec - now.tv_usec;
+                                tv.tv_sec  = end.tv_sec - now.tv_sec - 1;
+                            } else {
+                                tv.tv_usec = end.tv_usec - now.tv_usec;
+                                tv.tv_sec  = end.tv_sec - now.tv_sec;
+                            }
+                            FD_SET(socket, &rfds);
+                            continue;
+                        }
+                        LOG(log_error, logtype_afpd, "select: %s", strerror(errno));
+                        stored = -1;
+                        goto exit;
+                    }
+                } /* while (select) */
+                continue;
+            } /* switch (errno) */
+            LOG(log_error, logtype_afpd, "read: %s", strerror(errno));
+            stored = -1;
+            goto exit;
+        } /* (len == -1) */
+        else if (len > 0)
+            stored += len;
+        else
+            break;
+    } /* while (stored < length) */
+
+exit:
+    if (setnonblocking) {
+        if (setnonblock(socket, 0) != 0)
+            return -1;
+    }
+
+    if (len == -1 && stored == 0)
+        /* last read or select got an error and we haven't got yet anything => return -1*/
+        return -1;
+    return stored;
+}
+
+/*!
+ * non-blocking drop-in replacement for read with timeout using select
+ *
+ * @param socket          (r)  socket, if in blocking mode, pass "setnonblocking" arg as 1
+ * @param data            (rw) buffer for the read data
+ * @param lenght          (r)  how many bytes to read
+ * @param setnonblocking  (r)  when non-zero this func will enable and disable non blocking
+ *                             io mode for the socket
+ * @param timeout         (r)  number of seconds to try reading
+ *
+ * @returns number of bytes actually read or -1 on fatal error
+ */
+ssize_t writet(int socket, void *data, const size_t length, int setnonblocking, int timeout)
+{
+    size_t stored = 0;
+    ssize_t len = 0;
+    struct timeval now, end, tv;
+    fd_set rfds;
+    int ret;
+
+    if (setnonblocking) {
+        if (setnonblock(socket, 1) != 0)
+            return -1;
+    }
+
+    /* Calculate end time */
+    (void)gettimeofday(&now, NULL);
+    end = now;
+    end.tv_sec += timeout;
+
+    while (stored < length) {
+        len = write(socket, (char *) data + stored, length - stored);
+        if (len == -1) {
+            switch (errno) {
+            case EINTR:
+                continue;
+            case EAGAIN:
+                FD_ZERO(&rfds);
+                FD_SET(socket, &rfds);
+                tv.tv_usec = 0;
+                tv.tv_sec  = timeout;
+                        
+                while ((ret = select(socket + 1, &rfds, NULL, NULL, &tv)) < 1) {
+                    switch (ret) {
+                    case 0:
+                        LOG(log_warning, logtype_afpd, "select timeout %d s", timeout);
+                        goto exit;
+
+                    default: /* -1 */
+                        if (errno == EINTR) {
+                            (void)gettimeofday(&now, NULL);
+                            if (now.tv_sec >= end.tv_sec && now.tv_usec >= end.tv_usec) {
+                                LOG(log_warning, logtype_afpd, "select timeout %d s", timeout);
+                                goto exit;
+                            }
+                            if (now.tv_usec > end.tv_usec) {
+                                tv.tv_usec = 1000000 + end.tv_usec - now.tv_usec;
+                                tv.tv_sec  = end.tv_sec - now.tv_sec - 1;
+                            } else {
+                                tv.tv_usec = end.tv_usec - now.tv_usec;
+                                tv.tv_sec  = end.tv_sec - now.tv_sec;
+                            }
+                            FD_ZERO(&rfds);
+                            FD_SET(socket, &rfds);
+                            continue;
+                        }
+                        LOG(log_error, logtype_afpd, "select: %s", strerror(errno));
+                        stored = -1;
+                        goto exit;
+                    }
+                } /* while (select) */
+                continue;
+            } /* switch (errno) */
+            LOG(log_error, logtype_afpd, "read: %s", strerror(errno));
+            stored = -1;
+            goto exit;
+        } /* (len == -1) */
+        else if (len > 0)
+            stored += len;
+        else
+            break;
+    } /* while (stored < length) */
+
+exit:
+    if (setnonblocking) {
+        if (setnonblock(socket, 0) != 0)
+            return -1;
+    }
+
+    if (len == -1 && stored == 0)
+        /* last read or select got an error and we haven't got yet anything => return -1*/
+        return -1;
+    return stored;
+}
+
 /*!
  * @brief convert an IPv4 or IPv6 address to a static string using inet_ntop
  *
@@ -134,7 +340,7 @@ unsigned int getip_port(const struct sockaddr  *sa)
  * @param  ai        (rw) pointer to an struct sockaddr
  * @parma  mask      (r) number of maskbits
  */
-void apply_ip_mask(struct sockaddr *sa, uint32_t mask)
+void apply_ip_mask(struct sockaddr *sa, int mask)
 {
 
     switch (sa->sa_family) {
@@ -201,3 +407,239 @@ int compare_ip(const struct sockaddr *sa1, const struct sockaddr *sa2)
 
     return ret;
 }
+
+#define POLL_FD_SET_STARTSIZE 512
+#define POLL_FD_SET_INCREASE  128
+/*!
+ * Add a fd to a dynamic pollfd array that is allocated and grown as needed
+ *
+ * This uses an additional array of struct polldata which stores type information
+ * (enum fdtype) and a pointer to anciliary user data.
+ *
+ * 1. Allocate the arrays with an intial size of [POLL_FD_SET_STARTSIZE] if
+ *    *fdsetp is NULL.
+ * 2. Grow array as needed
+ * 3. Fill in both array elements and increase count of used elements
+ * 
+ * @param fdsetp      (rw) pointer to callers pointer to the pollfd array
+ * @param polldatap   (rw) pointer to callers pointer to the polldata array
+ * @param fdset_usedp (rw) pointer to an int with the number of used elements
+ * @param fdset_sizep (rw) pointer to an int which stores the array sizes
+ * @param fd          (r)  file descriptor to add to the arrays
+ * @param fdtype      (r)  type of fd, currently IPC_FD or LISTEN_FD
+ * @param data        (rw) pointer to data the caller want to associate with an fd
+ */
+void fdset_add_fd(struct pollfd **fdsetp,
+                  struct polldata **polldatap,
+                  int *fdset_usedp,
+                  int *fdset_sizep,
+                  int fd,
+                  enum fdtype fdtype,
+                  void *data)
+{
+    struct pollfd *fdset = *fdsetp;
+    struct polldata *polldata = *polldatap;
+    int fdset_size = *fdset_sizep;
+
+    LOG(log_debug, logtype_default, "fdset_add_fd: adding fd %i in slot %i", fd, *fdset_usedp);
+
+    if (fdset == NULL) { /* 1 */
+        /* Initialize with space for 512 fds */
+        fdset = calloc(POLL_FD_SET_STARTSIZE, sizeof(struct pollfd));
+        if (! fdset)
+            exit(EXITERR_SYS);
+
+        polldata = calloc(POLL_FD_SET_STARTSIZE, sizeof(struct polldata));
+        if (! polldata)
+            exit(EXITERR_SYS);
+
+        fdset_size = 512;
+        *fdset_sizep = fdset_size;
+        *fdsetp = fdset;
+        *polldatap = polldata;
+    }
+
+    if (*fdset_usedp >= fdset_size) { /* 2 */
+        fdset = realloc(fdset, sizeof(struct pollfd) * (fdset_size + POLL_FD_SET_INCREASE));
+        if (fdset == NULL)
+            exit(EXITERR_SYS);
+
+        polldata = realloc(polldata, sizeof(struct polldata) * (fdset_size + POLL_FD_SET_INCREASE));
+        if (polldata == NULL)
+            exit(EXITERR_SYS);
+
+        fdset_size += POLL_FD_SET_INCREASE;
+        *fdset_sizep = fdset_size;
+        *fdsetp = fdset;
+        *polldatap = polldata;
+    }
+
+    /* 3 */
+    fdset[*fdset_usedp].fd = fd;
+    fdset[*fdset_usedp].events = POLLIN;
+    polldata[*fdset_usedp].fdtype = fdtype;
+    polldata[*fdset_usedp].data = data;
+    (*fdset_usedp)++;
+}
+
+/*!
+ * Remove a fd from our pollfd array
+ *
+ * 1. Search fd
+ * 2. If we remove the last array elemnt, just decrease count
+ * 3. If found move all following elements down by one
+ * 4. Decrease count of used elements in array
+ *
+ * This currently doesn't shrink the allocated storage of the array.
+ *
+ * @param fdsetp      (rw) pointer to callers pointer to the pollfd array
+ * @param polldatap   (rw) pointer to callers pointer to the polldata array
+ * @param fdset_usedp (rw) pointer to an int with the number of used elements
+ * @param fdset_sizep (rw) pointer to an int which stores the array sizes
+ * @param fd          (r)  file descriptor to remove from the arrays
+ */
+void fdset_del_fd(struct pollfd **fdsetp,
+                  struct polldata **polldatap,
+                  int *fdset_usedp,
+                  int *fdset_sizep _U_,
+                  int fd)
+{
+    struct pollfd *fdset = *fdsetp;
+    struct polldata *polldata = *polldatap;
+
+    for (int i = 0; i < *fdset_usedp; i++) {
+        if (fdset[i].fd == fd) { /* 1 */
+            if (i < (*fdset_usedp - 1)) { /* 2 */
+                memmove(&fdset[i], &fdset[i+1], (*fdset_usedp - 1) * sizeof(struct pollfd)); /* 3 */
+                memmove(&polldata[i], &polldata[i+1], (*fdset_usedp - 1) * sizeof(struct polldata)); /* 3 */
+            }
+            (*fdset_usedp)--;
+            break;
+        }
+    }
+}
+
+/* Length of the space taken up by a padded control message of length len */
+#ifndef CMSG_SPACE
+#define CMSG_SPACE(len) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + __CMSG_ALIGN(len))
+#endif
+
+/*
+ * Receive a fd on a suitable socket
+ * @args fd          (r) PF_UNIX socket to receive on
+ * @args nonblocking (r) 0: fd is in blocking mode - 1: fd is nonblocking, poll for 1 sec
+ * @returns fd on success, -1 on error
+ */
+int recv_fd(int fd, int nonblocking)
+{
+    int ret;
+    struct msghdr msgh;
+    struct iovec iov[1];
+    struct cmsghdr *cmsgp = NULL;
+    char buf[CMSG_SPACE(sizeof(int))];
+    char dbuf[80];
+    struct pollfd pollfds[1];
+
+    pollfds[0].fd = fd;
+    pollfds[0].events = POLLIN;
+
+    memset(&msgh,0,sizeof(msgh));
+    memset(buf,0,sizeof(buf));
+
+    msgh.msg_name = NULL;
+    msgh.msg_namelen = 0;
+
+    msgh.msg_iov = iov;
+    msgh.msg_iovlen = 1;
+
+    iov[0].iov_base = dbuf;
+    iov[0].iov_len = sizeof(dbuf);
+
+    msgh.msg_control = buf;
+    msgh.msg_controllen = sizeof(buf);
+
+    if (nonblocking) {
+        do {
+            ret = poll(pollfds, 1, 2000); /* poll 2 seconds, evtl. multipe times (EINTR) */
+        } while ( ret == -1 && errno == EINTR );
+        if (ret != 1)
+            return -1;
+        ret = recvmsg(fd, &msgh, 0);
+    } else {
+        do  {
+            ret = recvmsg(fd, &msgh, 0);
+        } while ( ret == -1 && errno == EINTR );
+    }
+
+    if ( ret == -1 ) {
+        return -1;
+    }
+
+    for ( cmsgp = CMSG_FIRSTHDR(&msgh); cmsgp != NULL; cmsgp = CMSG_NXTHDR(&msgh,cmsgp) ) {
+        if ( cmsgp->cmsg_level == SOL_SOCKET && cmsgp->cmsg_type == SCM_RIGHTS ) {
+            return *(int *) CMSG_DATA(cmsgp);
+        }
+    }
+
+    if ( ret == sizeof (int) )
+        errno = *(int *)dbuf; /* Rcvd errno */
+    else
+        errno = ENOENT;    /* Default errno */
+
+    return -1;
+}
+
+/*
+ * Send a fd across a suitable socket
+ */
+int send_fd(int socket, int fd)
+{
+    int ret;
+    struct msghdr msgh;
+    struct iovec iov[1];
+    struct cmsghdr *cmsgp = NULL;
+    char *buf;
+    size_t size;
+    int er=0;
+
+    size = CMSG_SPACE(sizeof fd);
+    buf = malloc(size);
+    if (!buf) {
+        LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));
+        return -1;
+    }
+
+    memset(&msgh,0,sizeof (msgh));
+    memset(buf,0, size);
+
+    msgh.msg_name = NULL;
+    msgh.msg_namelen = 0;
+
+    msgh.msg_iov = iov;
+    msgh.msg_iovlen = 1;
+
+    iov[0].iov_base = &er;
+    iov[0].iov_len = sizeof(er);
+
+    msgh.msg_control = buf;
+    msgh.msg_controllen = size;
+
+    cmsgp = CMSG_FIRSTHDR(&msgh);
+    cmsgp->cmsg_level = SOL_SOCKET;
+    cmsgp->cmsg_type = SCM_RIGHTS;
+    cmsgp->cmsg_len = CMSG_LEN(sizeof(fd));
+
+    *((int *)CMSG_DATA(cmsgp)) = fd;
+    msgh.msg_controllen = cmsgp->cmsg_len;
+
+    do  {
+        ret = sendmsg(socket,&msgh, 0);
+    } while ( ret == -1 && errno == EINTR );
+    if (ret == -1) {
+        LOG(log_error, logtype_cnid, "error in sendmsg: %s", strerror(errno));
+        free(buf);
+        return -1;
+    }
+    free(buf);
+    return 0;
+}
diff --git a/libatalk/util/test/.gitignore b/libatalk/util/test/.gitignore
deleted file mode 100644 (file)
index 3dd6d8e..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-Makefile
-Makefile.in
-logger_test
-test.log
-.deps
-.libs
-
-.gitignore
-logger_test.o
diff --git a/libatalk/util/test/Makefile.am b/libatalk/util/test/Makefile.am
deleted file mode 100644 (file)
index 569936b..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-bin_PROGRAMS = logger_test
-
-logger_test_SOURCES = logger_test.c
-logger_test_LDADD =  $(top_builddir)/libatalk/util/libutil.la
diff --git a/libatalk/util/test/logger_test.c b/libatalk/util/test/logger_test.c
deleted file mode 100644 (file)
index 97323a3..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#include <stdio.h>
-
-#include <atalk/boolean.h>
-#include <atalk/logger.h>
-
-int main(int argc, char *argv[])
-{
-  set_processname("logger_Test");
-#if 0
-  LOG(log_severe, logtype_logger, "Logging Test starting: this should only log to syslog");
-
-  /* syslog testing */
-  LOG(log_severe, logtype_logger, "Disabling syslog logging.");
-  unsetuplog("Default");
-  LOG(log_error, logtype_default, "This shouldn't log to syslog: LOG(log_error, logtype_default).");
-  LOG(log_error, logtype_logger, "This shouldn't log to syslog: LOG(log_error, logtype_logger).");
-  setuplog("Default LOG_INFO");
-  LOG(log_info, logtype_logger, "Set syslog logging to 'log_info', so this should log again. LOG(log_info, logtype_logger).");
-  LOG(log_error, logtype_logger, "This should log to syslog: LOG(log_error, logtype_logger).");
-  LOG(log_error, logtype_default, "This should log to syslog. LOG(log_error, logtype_default).");
-  LOG(log_debug, logtype_logger, "This shouldn't log to syslog. LOG(log_debug, logtype_logger).");
-  LOG(log_debug, logtype_default, "This shouldn't log to syslog. LOG(log_debug, logtype_default).");
-  LOG(log_severe, logtype_logger, "Disabling syslog logging.");
-  unsetuplog("Default");
-#endif
-  /* filelog testing */
-
-  setuplog("DSI log_maxdebug test.log");
-  LOG(log_info, logtype_dsi, "This should log.");
-  LOG(log_error, logtype_default, "This should not log.");
-
-  setuplog("Default log_debug test.log");
-  LOG(log_debug, logtype_default, "This should log.");
-  LOG(log_maxdebug, logtype_default, "This should not log.");
-
-  LOG(log_maxdebug, logtype_dsi, "This should still log.");
-
-  return 0;
-}
-
-
index 6231a03782c538809dd632593c0796bd9e57fc9a..9e71fcb4b71a3b7fd53479c4cebc2cef7cb3bb30 100644 (file)
@@ -1,5 +1,4 @@
 /*
-  $Id: unix.c,v 1.6 2010-02-28 22:29:16 didg Exp $
   Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
 
   This program is free software; you can redistribute it and/or modify
@@ -31,6 +30,8 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <dirent.h>
+#include <sys/time.h>
+#include <time.h>
 
 #include <atalk/adouble.h>
 #include <atalk/ea.h>
@@ -56,6 +57,26 @@ const char *getcwdpath(void)
         return strerror(errno);
 }
 
+/*!
+ * Takes a buffer with a path, strips slashs, returns basename
+ *
+ * @param p (rw) path
+ *        path may be
+ *          "[/][dir/[...]]file"
+ *        or
+ *          "[/][dir/[...]]dir/[/]"
+ *        Result is "file" or "dir" 
+ *
+ * @returns pointer to basename in path buffer, buffer is possibly modified
+ */
+char *stripped_slashes_basename(char *p)
+{
+    int i = strlen(p) - 1;
+    while (i > 0 && p[i] == '/')
+        p[i--] = 0;
+    return (strrchr(p, '/') ? strrchr(p, '/') + 1 : p);
+}
+
 /*!
  * @brief symlink safe chdir replacement
  *
@@ -129,3 +150,33 @@ int lchdir(const char *dir)
 
     return 0;
 }
+
+/*!
+ * Store n random bytes an buf
+ */
+void randombytes(void *buf, int n)
+{
+    char *p = (char *)buf;
+    int fd, i;
+    struct timeval tv;
+
+    if ((fd = open("/dev/urandom", O_RDONLY)) != -1) {
+        /* generate from /dev/urandom */
+        if (read(fd, buf, n) != n) {
+            close(fd);
+            fd = -1;
+        } else {
+            close(fd);
+            /* fd now != -1, so srandom wont be called below */
+        }
+    }
+
+    if (fd == -1) {
+        gettimeofday(&tv, NULL);
+        srandom((unsigned int)tv.tv_usec);
+        for (i=0 ; i < n ; i++)
+            p[i] = random() & 0xFF;
+    }
+
+    return;
+}
index ef438c5d278f5b103a968e8e41953c9cad4d5021..62afbc6559b490aa0d6c62a76380bc170aefe0c5 100644 (file)
@@ -139,16 +139,32 @@ static char * make_path_absolute(char *path, size_t bufsize)
     char       abspath[MAXPATHLEN];
     char       *p;
 
-    if (stat(path, &st) != 0) {
-        return NULL;
-    }
+    strlcpy(abspath, path, sizeof(abspath));
+
+    /* we might be called from `ad cp ...` with non existing target */
+    if (stat(abspath, &st) != 0) {
+        if (errno != ENOENT)
+            return NULL;
+
+        if (NULL == (p = strrchr(abspath, '/')) )
+            /* single component `ad cp SOURCEFILE TARGETFILE`, use "." instead */
+            strcpy(abspath, ".");
+        else
+            /* try without the last path element */
+            *p = '\0';
 
-    strlcpy (abspath, path, sizeof(abspath));
+        if (stat(abspath, &st) != 0) {
+            return NULL;
+        }
+    }
 
-    if (!S_ISDIR(st.st_mode)) {
-        if (NULL == (p=strrchr(abspath, '/')) )
+    if (S_ISREG(st.st_mode)) {
+        /* single file copy SOURCE */
+        if (NULL == (p = strrchr(abspath, '/')) )
+            /* no path, use "." instead */
             strcpy(abspath, ".");
         else
+            /* try without the last path element */
             *p = '\0';
     }
 
@@ -272,14 +288,16 @@ static int parseline ( char *buf, struct volinfo *vol)
         }
         break;
       case CNIDDBDPORT:
-        vol->v_dbd_port = atoi(value);
-        break;
-      case CNID_DBPATH:
-        if ((vol->v_dbpath = strdup(value)) == NULL) {
+        if ((vol->v_dbd_port = strdup(value)) == NULL) {
            fprintf (stderr, "strdup: %s", strerror(errno));
-            return -1;
+            return -1;            
         }
         break;
+      case CNID_DBPATH:
+          if ((vol->v_dbpath = malloc(MAXPATHLEN+1)) == NULL)
+              return -1;
+          strcpy(vol->v_dbpath, value);
+        break;
       case ADOUBLE_VER:
         if (strcasecmp(value, "v1") == 0) {
             vol->v_adouble = AD_VERSION1;
@@ -328,7 +346,7 @@ int loadvolinfo (char *path, struct volinfo *vol)
     char   volinfofile[MAXPATHLEN];
     char   buf[MAXPATHLEN];
     struct flock lock;
-    int    fd;
+    int    fd, len;
     FILE   *fp;
 
     if ( !path || !vol)
@@ -342,9 +360,16 @@ int loadvolinfo (char *path, struct volinfo *vol)
         return -1;
 
     if ((vol->v_path = strdup(volinfofile)) == NULL ) {
-       fprintf (stderr, "strdup: %s", strerror(errno));
+        fprintf (stderr, "strdup: %s", strerror(errno));
         return (-1);
     }
+    /* Remove trailing slashes */
+    len = strlen(vol->v_path);
+    while (len && (vol->v_path[len-1] == '/')) {
+        vol->v_path[len-1] = 0;
+        len--;
+    }
+
     strlcat(volinfofile, ".AppleDesktop/", sizeof(volinfofile));
     strlcat(volinfofile, VOLINFOFILE, sizeof(volinfofile));
 
@@ -387,10 +412,63 @@ int loadvolinfo (char *path, struct volinfo *vol)
     if ((vol->v_flags & AFPVOL_INV_DOTS))
         vol->v_ad_options |= ADVOL_INVDOTS;
 
+    vol->retaincount = 1;
+
     fclose(fp);
     return 0;
 }
 
+/*!
+ * Allocate a struct volinfo object for refcounting usage with retain and close, and
+ * call loadvolinfo with it
+ */
+struct volinfo *allocvolinfo(char *path)
+{
+    struct volinfo *p = malloc(sizeof(struct volinfo));
+    if (p == NULL)
+        return NULL;
+
+    if (loadvolinfo(path, p) == -1)
+        return NULL;
+
+    p->malloced = 1;
+
+    return p;
+}
+
+void retainvolinfo(struct volinfo *vol)
+{
+    vol->retaincount++;
+}
+
+/*!
+ * Decrement retain count, free resources when retaincount reaches 0
+ */
+int closevolinfo(struct volinfo *volinfo)
+{
+    if (volinfo->retaincount <= 0)
+        abort();
+
+    volinfo->retaincount--;
+
+    if (volinfo->retaincount == 0) {
+        free(volinfo->v_name); volinfo->v_name = NULL;
+        free(volinfo->v_path); volinfo->v_path = NULL;
+        free(volinfo->v_cnidscheme); volinfo->v_cnidscheme = NULL;
+        free(volinfo->v_dbpath); volinfo->v_dbpath = NULL;
+        free(volinfo->v_volcodepage); volinfo->v_volcodepage = NULL;
+        free(volinfo->v_maccodepage); volinfo->v_maccodepage = NULL;
+        free(volinfo->v_dbd_host); volinfo->v_dbd_host = NULL;
+        free(volinfo->v_dbd_port); volinfo->v_dbd_port = NULL;
+        if (volinfo->malloced) {
+            volinfo->malloced = 0;
+            free(volinfo);
+        }
+    }
+
+    return 0;
+}
+
 /*
  * Save the volume options to a file, used by shell utilities. Writing the file
  * everytime a volume is opened is unnecessary, but it shouldn't hurt much.
index d48caa315961ace34097d50d2118518a848b3f23..9b8d14374c8e4fbb6aeaa86716ce7aff2af73c09 100644 (file)
@@ -1,10 +1,9 @@
-
-# Makefile.am for libatalk/adouble/
+# Makefile.am for libatalk/vfs/
 
 noinst_LTLIBRARIES = libvfs.la
 
 libvfs_la_SOURCES = vfs.c unix.c ea.c sys_ea.c ea_sys.c
 
-if USE_NFSv4_ACLS
+if HAVE_ACLS
 libvfs_la_SOURCES += acl.c
 endif
index 4b118607155a7a9430e5798da1ac6b0afad6b839..1396ce34c809d98e5083f1b96ba16fe49180f266 100644 (file)
@@ -1,6 +1,6 @@
 /*
-  $Id: acl.c,v 1.2 2009-11-26 18:17:12 franklahm Exp $
   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
+  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
 
   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
 #endif /* HAVE_CONFIG_H */
 
 #include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <sys/acl.h>
 
 #include <atalk/afp.h>
 #include <atalk/util.h>
 #include <atalk/logger.h>
+#include <atalk/errchk.h>
+#include <atalk/acl.h>
 
-/* Get ACL. Allocates storage as needed. Caller must free.
- * Returns no of ACEs or -1 on error.  */
-int get_nfsv4_acl(const char *name, ace_t **retAces)
-{
-    int ace_count = -1;
-    ace_t *aces;
-
-    *retAces = NULL;
-    ace_count = acl(name, ACE_GETACLCNT, 0, NULL);
-    if (ace_count <= 0) {
-       LOG(log_error, logtype_afpd, "get_nfsv4_acl: acl(ACE_GETACLCNT) error");
-        return -1;
-    }
-
-    aces = malloc(ace_count * sizeof(ace_t));
-    if (aces == NULL) {
-       LOG(log_error, logtype_afpd, "get_nfsv4_acl: malloc error");
-       return -1;
-    }
-
-    if ( (acl(name, ACE_GETACL, ace_count, aces)) == -1 ) {
-       LOG(log_error, logtype_afpd, "get_nfsv4_acl: acl(ACE_GETACL) error");
-       free(aces);
-       return -1;
-    }
-
-    LOG(log_debug9, logtype_afpd, "get_nfsv4_acl: file: %s -> No. of ACEs: %d", name, ace_count);
-    *retAces = aces;
-
-    return ace_count;
-}
+#ifdef HAVE_SOLARIS_ACLS
 
 /* Removes all non-trivial ACLs from object. Returns full AFPERR code. */
-int remove_acl(const char *name)
+int remove_acl_vfs(const char *name)
 {
     int ret,i, ace_count, trivial_aces, new_aces_count;
     ace_t *old_aces = NULL;
@@ -112,3 +85,51 @@ exit:
     LOG(log_debug9, logtype_afpd, "remove_acl: END");
     return ret;
 }
+
+#endif  /* HAVE_SOLARIS_ACLS */
+
+#ifdef HAVE_POSIX_ACLS
+/*!
+ * Remove any ACL_USER, ACL_GROUP or ACL_TYPE_DEFAULT ACEs from an object
+ *
+ * @param name  (r) filesystem object name
+ *
+ * @returns AFP error code, AFP_OK (= 0) on success, AFPERR_MISC on error
+ */
+int remove_acl_vfs(const char *name)
+{
+    EC_INIT;
+
+    struct stat st;
+    acl_t acl = NULL;
+    acl_entry_t e;
+    acl_tag_t tag;
+    int entry_id = ACL_FIRST_ENTRY;
+
+
+    /* Remove default ACL if it's a dir */
+    EC_ZERO_LOG_ERR(stat(name, &st), AFPERR_MISC);
+    if (S_ISDIR(st.st_mode)) {
+        EC_NULL_LOG_ERR(acl = acl_init(0), AFPERR_MISC);
+        EC_ZERO_LOG_ERR(acl_set_file(name, ACL_TYPE_DEFAULT, acl), AFPERR_MISC);
+        EC_ZERO_LOG_ERR(acl_free(acl), AFPERR_MISC);
+        acl = NULL;
+    }
+
+    /* Now get ACL and remove ACL_USER or ACL_GROUP entries, then re-set the ACL again */
+    EC_NULL_LOG_ERR(acl = acl_get_file(name, ACL_TYPE_ACCESS), AFPERR_MISC);
+    for ( ; acl_get_entry(acl, entry_id, &e) == 1; entry_id = ACL_NEXT_ENTRY) {
+        EC_ZERO_LOG_ERR(acl_get_tag_type(e, &tag), AFPERR_MISC);
+        if (tag == ACL_USER || tag == ACL_GROUP)
+            EC_ZERO_LOG_ERR(acl_delete_entry(acl, e), AFPERR_MISC);
+    }
+    EC_ZERO_LOG_ERR(acl_calc_mask(&acl), AFPERR_MISC);
+    EC_ZERO_LOG_ERR(acl_valid(acl), AFPERR_MISC);
+    EC_ZERO_LOG_ERR(acl_set_file(name, ACL_TYPE_ACCESS, acl), AFPERR_MISC);
+
+EC_CLEANUP:
+    if (acl) acl_free(acl);
+
+    EC_EXIT;
+}
+#endif /* HAVE_POSIX_ACLS */
index 0653191f816f335ca9959e85d41ab3e346a1faad..fa3a010926c898ff1a1688d0e631e5cf13f64f47 100644 (file)
@@ -1,5 +1,4 @@
 /*
-  $Id: ea.c,v 1.22 2010-04-18 11:11:17 franklahm Exp $
   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
 
   This program is free software; you can redistribute it and/or modify
@@ -1097,6 +1096,7 @@ int get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
 
             if (read(fd, rbuf, toread) != (ssize_t)toread) {
                 LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname);
+                close(fd);
                 ret = AFPERR_MISC;
                 break;
             }
index f9b305aa89764360454e95b6d9479bf2a2b85d98..77ab783b04c8d3546f20ecb66e5d6335e387ae82 100644 (file)
@@ -238,9 +238,16 @@ int sys_list_eas(VFS_FUNC_ARGS_EA_LIST)
         case OPEN_NOFOLLOW_ERRNO:
             /* its a symlink and client requested O_NOFOLLOW */
             ret = AFPERR_BADTYPE;
+            goto exit;
+#ifdef HAVE_ATTROPEN            /* Solaris */
+        case ENOATTR:
+            ret = AFP_OK;
+            goto exit;
+#endif
         default:
             LOG(log_error, logtype_afpd, "sys_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
             ret= AFPERR_MISC;
+            goto exit;
     }
     
     ptr = buf;
@@ -318,15 +325,20 @@ int sys_set_ea(VFS_FUNC_ARGS_EA_SET)
         switch(errno) {
         case OPEN_NOFOLLOW_ERRNO:
             /* its a symlink and client requested O_NOFOLLOW  */
-            LOG(log_debug, logtype_afpd, "sys_set_ea(%s/%s): encountered symlink with kXAttrNoFollow",
-                uname, attruname);
+            LOG(log_debug, logtype_afpd, "sys_set_ea(\"%s/%s\", ea:'%s'): encountered symlink with kXAttrNoFollow",
+                getcwdpath(), uname, attruname);
             return AFP_OK;
         case EEXIST:
-            LOG(log_debug, logtype_afpd, "sys_set_ea(%s/%s): EA already exists",
-                uname, attruname);
+            LOG(log_debug, logtype_afpd, "sys_set_ea(\"%s/%s\", ea:'%s'): EA already exists",
+                getcwdpath(), uname, attruname);
             return AFPERR_EXIST;
         default:
-            LOG(log_error, logtype_afpd, "sys_set_ea(%s/%s): error: %s", uname, attruname, strerror(errno));
+            LOG(log_error, logtype_afpd, "sys_set_ea(\"%s/%s\", ea:'%s', size: %u, flags: %s|%s|%s): %s",
+                getcwdpath(), uname, attruname, attrsize, 
+                oflag & O_CREAT ? "XATTR_CREATE" : "-",
+                oflag & O_TRUNC ? "XATTR_REPLACE" : "-",
+                oflag & O_NOFOLLOW ? "O_NOFOLLOW" : "-",
+                strerror(errno));
             return AFPERR_MISC;
         }
     }
index 900eb07e5591ce1e08ab4aeadbc633d575f2038a..3db20b239cc69f4b2a39695d85ced1653dbf6daf 100644 (file)
@@ -707,7 +707,7 @@ static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
        }
 
        if (closedir(dirp) == -1) {
-               LOG(log_debug, logtype_default, "closedir dirp failed: %s\n",strerror(errno));
+               LOG(log_error, logtype_default, "closedir dirp: %s",strerror(errno));
                return -1;
        }
        return len;
@@ -725,7 +725,9 @@ static int solaris_attropen(const char *path, const char *attrpath, int oflag, m
 {
        int filedes = attropen(path, attrpath, oflag, mode);
        if (filedes == -1) {
-               LOG(log_maxdebug, logtype_default, "attropen FAILED: path: %s, name: %s, errno: %s\n",path,attrpath,strerror(errno));
+        if (errno != ENOENT)
+            LOG(log_error, logtype_default, "attropen(\"%s\", ea:'%s'): %s",
+                path, attrpath, strerror(errno));
         errno = ENOATTR;
        }
        return filedes;
@@ -735,7 +737,8 @@ static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
 {
        int filedes = openat(fildes, path, oflag, mode);
        if (filedes == -1) {
-               LOG(log_maxdebug, logtype_default, "openat FAILED: fd: %s, path: %s, errno: %s\n",filedes,path,strerror(errno));
+               LOG(log_error, logtype_default, "openat(\"%s\"): %s",
+            path, strerror(errno));
        }
        return filedes;
 }
@@ -745,7 +748,8 @@ static int solaris_write_xattr(int attrfd, const char *value, size_t size)
        if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
                return 0;
        } else {
-               LOG(log_maxdebug, logtype_default, "solaris_write_xattr FAILED!\n");
+               LOG(log_error, logtype_default, "solaris_write_xattr: %s",
+            strerror(errno));
                return -1;
        }
 }
index 4812e96421ad49846bc501d042170ff332423bf0..229f1b7a216e25309c3c581c5c9fcc6e3a1ca6e5 100644 (file)
@@ -1,9 +1,6 @@
 /*
- * $Id: unix.c,v 1.11 2010-04-18 16:14:51 hat001 Exp $
- *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
- *
  */
 
 #ifdef HAVE_CONFIG_H
@@ -24,6 +21,7 @@
 #include <atalk/volume.h>
 #include <atalk/logger.h>
 #include <atalk/unix.h>
+#include <atalk/acl.h>
 
 /* -----------------------------
    a dropbox is a folder where w is set but not r eg:
@@ -103,15 +101,15 @@ int setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask)
 /*
  * @brief system rmdir with afp error code.
  *
- * Supports *at semantics (cf openat) if HAVE_RENAMEAT. Pass dirfd=-1 to ignore this.
+ * Supports *at semantics (cf openat) if HAVE_ATFUNCS. Pass dirfd=-1 to ignore this.
  */
 int netatalk_rmdir_all_errors(int dirfd, const char *name)
 {
     int err;
 
-#ifdef HAVE_RENAMEAT
+#ifdef HAVE_ATFUNCS
     if (dirfd == -1)
-        dirfd = ATFD_CWD;
+        dirfd = AT_FDCWD;
     err = unlinkat(dirfd, name, AT_REMOVEDIR);
 #else
     err = rmdir(name);
@@ -138,7 +136,7 @@ int netatalk_rmdir_all_errors(int dirfd, const char *name)
 /*
  * @brief System rmdir with afp error code, but ENOENT is not an error.
  *
- * Supports *at semantics (cf openat) if HAVE_RENAMEAT. Pass dirfd=-1 to ignore this.
+ * Supports *at semantics (cf openat) if HAVE_ATFUNCS. Pass dirfd=-1 to ignore this.
  */
 int netatalk_rmdir(int dirfd, const char *name)
 {
@@ -190,7 +188,7 @@ char *fullpathname(const char *name)
  **************************************************************************/
 
 /* 
- * Supports *at semantics if HAVE_RENAMEAT, pass dirfd=-1 to ignore this
+ * Supports *at semantics if HAVE_ATFUNCS, pass dirfd=-1 to ignore this
  */
 int copy_file(int dirfd, const char *src, const char *dst, mode_t mode)
 {
@@ -201,9 +199,9 @@ int copy_file(int dirfd, const char *src, const char *dst, mode_t mode)
     size_t  buflen;
     char   filebuf[8192];
 
-#ifdef HAVE_RENAMEAT
+#ifdef HAVE_ATFUNCS
     if (dirfd == -1)
-        dirfd = ATFD_CWD;
+        dirfd = AT_FDCWD;
     sfd = openat(dirfd, src, O_RDONLY);
 #else
     sfd = open(src, O_RDONLY);
@@ -214,7 +212,7 @@ int copy_file(int dirfd, const char *src, const char *dst, mode_t mode)
         return -1;
     }
 
-    if ((dfd = open(dst, O_WRONLY | O_CREAT | O_EXCL, mode)) < 0) {
+    if ((dfd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, mode)) < 0) {
         LOG(log_error, logtype_afpd, "copy_file('%s'/'%s'): open '%s' error: %s",
             src, dst, dst, strerror(errno));
         ret = -1;
@@ -269,7 +267,7 @@ exit:
  */
 int netatalk_unlinkat(int dirfd, const char *name)
 {
-#ifdef HAVE_RENAMEAT
+#ifdef HAVE_ATFUNCS
     if (dirfd == -1)
         dirfd = AT_FDCWD;
 
@@ -298,17 +296,17 @@ int netatalk_unlinkat(int dirfd, const char *name)
 /*
  * @brief This is equivalent of unix rename()
  *
- * unix_rename mulitplexes rename and renameat. If we dont HAVE_RENAMEAT, sfd and dfd
+ * unix_rename mulitplexes rename and renameat. If we dont HAVE_ATFUNCS, sfd and dfd
  * are ignored.
  *
- * @param sfd        (r) if we HAVE_RENAMEAT, -1 gives AT_FDCWD
+ * @param sfd        (r) if we HAVE_ATFUNCS, -1 gives AT_FDCWD
  * @param oldpath    (r) guess what
  * @param dfd        (r) same as sfd
  * @param newpath    (r) guess what
  */
 int unix_rename(int sfd, const char *oldpath, int dfd, const char *newpath)
 {
-#ifdef HAVE_RENAMEAT
+#ifdef HAVE_ATFUNCS
     if (sfd == -1)
         sfd = AT_FDCWD;
     if (dfd == -1)
@@ -319,7 +317,7 @@ int unix_rename(int sfd, const char *oldpath, int dfd, const char *newpath)
 #else
     if (rename(oldpath, newpath) < 0)
         return -1;
-#endif  /* HAVE_RENAMEAT */
+#endif  /* HAVE_ATFUNCS */
 
     return 0;
 }
@@ -327,15 +325,15 @@ int unix_rename(int sfd, const char *oldpath, int dfd, const char *newpath)
 /* 
  * @brief stat/fsstatat multiplexer
  *
- * statat mulitplexes stat and fstatat. If we dont HAVE_RENAMEAT, dirfd is ignored.
+ * statat mulitplexes stat and fstatat. If we dont HAVE_ATFUNCS, dirfd is ignored.
  *
- * @param dirfd   (r) Only used if HAVE_RENAMEAT, ignored else, -1 gives AT_FDCWD
+ * @param dirfd   (r) Only used if HAVE_ATFUNCS, ignored else, -1 gives AT_FDCWD
  * @param path    (r) pathname
  * @param st      (rw) pointer to struct stat
  */
 int statat(int dirfd, const char *path, struct stat *st)
 {
-#ifdef HAVE_RENAMEAT
+#ifdef HAVE_ATFUNCS
     if (dirfd == -1)
         dirfd = AT_FDCWD;
     return (fstatat(dirfd, path, st, 0));
@@ -350,15 +348,15 @@ int statat(int dirfd, const char *path, struct stat *st)
 /* 
  * @brief lstat/fsstatat multiplexer
  *
- * lstatat mulitplexes lstat and fstatat. If we dont HAVE_RENAMEAT, dirfd is ignored.
+ * lstatat mulitplexes lstat and fstatat. If we dont HAVE_ATFUNCS, dirfd is ignored.
  *
- * @param dirfd   (r) Only used if HAVE_RENAMEAT, ignored else, -1 gives AT_FDCWD
+ * @param dirfd   (r) Only used if HAVE_ATFUNCS, ignored else, -1 gives AT_FDCWD
  * @param path    (r) pathname
  * @param st      (rw) pointer to struct stat
  */
 int lstatat(int dirfd, const char *path, struct stat *st)
 {
-#ifdef HAVE_RENAMEAT
+#ifdef HAVE_ATFUNCS
     if (dirfd == -1)
         dirfd = AT_FDCWD;
     return (fstatat(dirfd, path, st, AT_SYMLINK_NOFOLLOW));
index dadedc5bbee20c21ac384b0ee7d19f435656c069..3156afe33191433b0ac43cf7db77d1cb4d05e43e 100644 (file)
 #include "config.h"
 #endif /* HAVE_CONFIG_H */
 
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <libgen.h>
 
 #include <atalk/afp.h>    
 #include <atalk/adouble.h>
@@ -35,6 +39,9 @@
 #include <atalk/vfs.h>
 #include <atalk/directory.h>
 #include <atalk/unix.h>
+#include <atalk/errchk.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
 
 struct perm {
     uid_t uid;
@@ -320,7 +327,67 @@ static int RF_renamefile_adouble(VFS_FUNC_ARGS_RENAMEFILE)
        return 0;
 }
 
-#ifdef HAVE_NFSv4_ACLS
+static int RF_copyfile_adouble(VFS_FUNC_ARGS_COPYFILE)
+/* const struct vol *vol, int sfd, const char *src, const char *dst */
+{
+    EC_INIT;
+    bstring s = NULL, d = NULL;
+    char *dup1 = NULL;
+    char *dup2 = NULL;
+    char *dup3 = NULL;
+    char *dup4 = NULL;
+    const char *name = NULL;
+    const char *dir = NULL;
+
+    struct stat st;
+    EC_ZERO(stat(dst, &st));
+
+    if (S_ISDIR(st.st_mode)) {
+        /* build src path to AppleDouble file*/
+        EC_NULL(s = bfromcstr(src));
+        EC_ZERO(bcatcstr(s, "/.AppleDouble/.Parent"));
+
+        /* build dst path to AppleDouble file*/
+        EC_NULL(d = bfromcstr(dst));
+        EC_ZERO(bcatcstr(d, "/.AppleDouble/.Parent"));
+    } else {
+        /* get basename */
+
+        /* build src path to AppleDouble file*/
+        EC_NULL(dup1 = strdup(src));
+        EC_NULL(name = basename(strdup(dup1)));
+
+        EC_NULL(dup2 = strdup(src));
+        EC_NULL(dir = dirname(dup2));
+        EC_NULL(s = bfromcstr(dir));
+        EC_ZERO(bcatcstr(s, "/.AppleDouble/"));
+        EC_ZERO(bcatcstr(s, name));
+
+        /* build dst path to AppleDouble file*/
+        EC_NULL(dup4 = strdup(dst));
+        EC_NULL(name = basename(strdup(dup4)));
+
+        EC_NULL(dup3 = strdup(dst));
+        EC_NULL(dir = dirname(dup3));
+        EC_NULL(d = bfromcstr(dir));
+        EC_ZERO(bcatcstr(d, "/.AppleDouble/"));
+        EC_ZERO(bcatcstr(d, name));
+    }
+
+    EC_ZERO(copy_file(sfd, cfrombstr(s), cfrombstr(d), 0666));
+
+EC_CLEANUP:
+    bdestroy(s);
+    bdestroy(d);
+    if (dup1) free(dup1);
+    if (dup2) free(dup2);
+    if (dup3) free(dup3);
+    if (dup4) free(dup4);
+
+    EC_EXIT;
+}
+
+#ifdef HAVE_SOLARIS_ACLS
 static int RF_solaris_acl(VFS_FUNC_ARGS_ACL)
 {
     static char buf[ MAXPATHLEN + 1];
@@ -358,20 +425,74 @@ static int RF_solaris_remove_acl(VFS_FUNC_ARGS_REMOVE_ACL)
        if (len < 0 || len >=  MAXPATHLEN)
            return AFPERR_MISC;
        /* remove ACL from .AppleDouble/.Parent first */
-       if ((ret = remove_acl(vol->ad_path(path, ADFLAGS_DIR))) != AFP_OK)
+       if ((ret = remove_acl_vfs(vol->ad_path(path, ADFLAGS_DIR))) != AFP_OK)
            return ret;
        /* now remove from .AppleDouble dir */
-       if ((ret = remove_acl(buf)) != AFP_OK)
+       if ((ret = remove_acl_vfs(buf)) != AFP_OK)
            return ret;
     } else
        /* remove ACL from ressource fork */
-       if ((ret = remove_acl(vol->ad_path(path, ADFLAGS_HF))) != AFP_OK)
+       if ((ret = remove_acl_vfs(vol->ad_path(path, ADFLAGS_HF))) != AFP_OK)
            return ret;
 
     return AFP_OK;
 }
 #endif
 
+#ifdef HAVE_POSIX_ACLS
+static int RF_posix_acl(VFS_FUNC_ARGS_ACL)
+{
+    EC_INIT;
+    static char buf[ MAXPATHLEN + 1];
+    struct stat st;
+    int len;
+
+    if (S_ISDIR(st.st_mode)) {
+        len = snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path);
+        if (len < 0 || len >=  MAXPATHLEN)
+            EC_FAIL;
+        /* set acl on .AppleDouble dir first */
+        EC_ZERO_LOG(acl_set_file(buf, type, acl));
+
+        if (type == ACL_TYPE_ACCESS)
+            /* set ACL on ressource fork (".Parent") too */
+            EC_ZERO_LOG(acl_set_file(vol->ad_path(path, ADFLAGS_DIR), type, acl));
+    } else {
+        /* set ACL on ressource fork */
+        EC_ZERO_LOG(acl_set_file(vol->ad_path(path, ADFLAGS_HF), type, acl));
+    }
+    
+EC_CLEANUP:
+    if (ret != 0)
+        return AFPERR_MISC;
+    return AFP_OK;
+}
+
+static int RF_posix_remove_acl(VFS_FUNC_ARGS_REMOVE_ACL)
+{
+    EC_INIT;
+    static char buf[ MAXPATHLEN + 1];
+    int len;
+
+    if (dir) {
+        len = snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path);
+        if (len < 0 || len >=  MAXPATHLEN)
+            return AFPERR_MISC;
+        /* remove ACL from .AppleDouble/.Parent first */
+        EC_ZERO_LOG_ERR(remove_acl_vfs(vol->ad_path(path, ADFLAGS_DIR)), AFPERR_MISC);
+
+        /* now remove from .AppleDouble dir */
+        EC_ZERO_LOG_ERR(remove_acl_vfs(buf), AFPERR_MISC);
+    } else {
+        /* remove ACL from ressource fork */
+        EC_ZERO_LOG_ERR(remove_acl_vfs(vol->ad_path(path, ADFLAGS_HF)), AFPERR_MISC);
+    }
+
+EC_CLEANUP:
+    EC_EXIT;
+}
+#endif
+
 /*********************************************************************************
  * sfm adouble format
  *********************************************************************************/
@@ -839,8 +960,10 @@ VFS_MFUNC(setdirowner, VFS_FUNC_ARGS_SETDIROWNER, VFS_FUNC_VARS_SETDIROWNER)
 VFS_MFUNC(deletefile, VFS_FUNC_ARGS_DELETEFILE, VFS_FUNC_VARS_DELETEFILE)
 VFS_MFUNC(renamefile, VFS_FUNC_ARGS_RENAMEFILE, VFS_FUNC_VARS_RENAMEFILE)
 VFS_MFUNC(copyfile, VFS_FUNC_ARGS_COPYFILE, VFS_FUNC_VARS_COPYFILE)
+#ifdef HAVE_ACLS
 VFS_MFUNC(acl, VFS_FUNC_ARGS_ACL, VFS_FUNC_VARS_ACL)
 VFS_MFUNC(remove_acl, VFS_FUNC_ARGS_REMOVE_ACL, VFS_FUNC_VARS_REMOVE_ACL)
+#endif
 VFS_MFUNC(ea_getsize, VFS_FUNC_ARGS_EA_GETSIZE, VFS_FUNC_VARS_EA_GETSIZE)
 VFS_MFUNC(ea_getcontent, VFS_FUNC_ARGS_EA_GETCONTENT, VFS_FUNC_VARS_EA_GETCONTENT)
 VFS_MFUNC(ea_list, VFS_FUNC_ARGS_EA_LIST, VFS_FUNC_VARS_EA_LIST)
@@ -868,8 +991,10 @@ static struct vfs_ops vfs_master_funcs = {
     vfs_deletefile,
     vfs_renamefile,
     vfs_copyfile,
+#ifdef HAVE_ACLS
     vfs_acl,
     vfs_remove_acl,
+#endif
     vfs_ea_getsize,
     vfs_ea_getcontent,
     vfs_ea_list,
@@ -892,7 +1017,7 @@ static struct vfs_ops netatalk_adouble = {
     /* vfs_setdirowner:   */ RF_setdirowner_adouble,
     /* vfs_deletefile:    */ RF_deletefile_adouble,
     /* vfs_renamefile:    */ RF_renamefile_adouble,
-    /* vfs_copyfile:      */ NULL,
+    /* vfs_copyfile:      */ RF_copyfile_adouble,
     NULL
 };
 
@@ -943,8 +1068,10 @@ static struct vfs_ops netatalk_ea_adouble = {
     /* vfs_deletefile:    */ ea_deletefile,
     /* vfs_renamefile:    */ ea_renamefile,
     /* vfs_copyfile       */ ea_copyfile,
+#ifdef HAVE_ACLS
     /* vfs_acl:           */ NULL,
     /* vfs_remove_acl     */ NULL,
+#endif
     /* vfs_getsize        */ get_easize,
     /* vfs_getcontent     */ get_eacontent,
     /* vfs_list           */ list_eas,
@@ -964,8 +1091,10 @@ static struct vfs_ops netatalk_ea_sys = {
     /* rf_deletefile:     */ NULL,
     /* rf_renamefile:     */ NULL,
     /* vfs_copyfile:      */ sys_ea_copyfile,
+#ifdef HAVE_ACLS
     /* rf_acl:            */ NULL,
     /* rf_remove_acl      */ NULL,
+#endif
     /* ea_getsize         */ sys_get_easize,
     /* ea_getcontent      */ sys_get_eacontent,
     /* ea_list            */ sys_list_eas,
@@ -977,7 +1106,7 @@ static struct vfs_ops netatalk_ea_sys = {
  * Tertiary VFS modules for ACLs
  */
 
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_SOLARIS_ACLS
 static struct vfs_ops netatalk_solaris_acl_adouble = {
     /* validupath:        */ NULL,
     /* rf_chown:          */ NULL,
@@ -996,6 +1125,25 @@ static struct vfs_ops netatalk_solaris_acl_adouble = {
 };
 #endif
 
+#ifdef HAVE_POSIX_ACLS
+static struct vfs_ops netatalk_posix_acl_adouble = {
+    /* validupath:        */ NULL,
+    /* rf_chown:          */ NULL,
+    /* rf_renamedir:      */ NULL,
+    /* rf_deletecurdir:   */ NULL,
+    /* rf_setfilmode:     */ NULL,
+    /* rf_setdirmode:     */ NULL,
+    /* rf_setdirunixmode: */ NULL,
+    /* rf_setdirowner:    */ NULL,
+    /* rf_deletefile:     */ NULL,
+    /* rf_renamefile:     */ NULL,
+    /* vfs_copyfile       */ NULL,
+    /* rf_acl:            */ RF_posix_acl,
+    /* rf_remove_acl      */ RF_posix_remove_acl,
+    NULL
+};
+#endif
+
 /* ---------------- */
 void initvol_vfs(struct vol *vol)
 {
@@ -1027,7 +1175,11 @@ void initvol_vfs(struct vol *vol)
     }
 
     /* ACLs */
-#ifdef HAVE_NFSv4_ACLS
+#ifdef HAVE_SOLARIS_ACLS
     vol->vfs_modules[2] = &netatalk_solaris_acl_adouble;
 #endif
+#ifdef HAVE_POSIX_ACLS
+    vol->vfs_modules[2] = &netatalk_posix_acl_adouble;
+#endif
+
 }
index 7b3c64edecdb1e62bf586399cc6e98de652231a8..2cc59cbbb6200f40785c98d22d2c1df944dc1050 100644 (file)
@@ -87,7 +87,7 @@ AC_DEFUN([AC_PATH_BDB],[
     trybdbdir=""
     dobdbsearch=yes
     bdb_search_dirs="/usr/local /usr"
-    search_subdirs="/ /db5 /db5.0 /db50 /db4.8 /db48 /db4.7 /db47 /db4.6 /db46 /db4"
+    search_subdirs="/ /db5 /db5.1 /db51 /db5.0 /db50 /db4.8 /db48 /db4.7 /db47 /db4.6 /db46 /db4"
 
     bdbfound=no
     savedcflags="$CFLAGS"
@@ -161,8 +161,10 @@ AC_DEFUN([AC_PATH_BDB],[
                             break;
                         fi
 
-                        dnl -- Search for 64bit lib in "lib" too
-                        if test x"$atalk_libname" = x"lib64" ; then
+                        dnl -- Search lib in "lib" too, as $atalk_libname might be set
+                        dnl -- to "lib64" or "lib/64" which would not be found above
+                        dnl -- if 64bit lib were installed in a dir named "lib"
+                        if test x"$atalk_libname" != x"lib" ; then
                            bdblibdir="${bdbdir}/lib"
                            bdbbindir="${bdbdir}/bin"
 
index 78ffb3eae7f3a7b12f052550c89e5065795a9a98..7673d4822589523e6eafb14e3d2050e02bf15b0c 100644 (file)
@@ -16,7 +16,6 @@ AC_DEFUN([AC_NETATALK_CONFIG_SUMMARY], [
                AC_MSG_RESULT([         Large file support (>2GB) for AFP3: $wx_largefile])
        fi
        AC_MSG_RESULT([         Extended Attributes: $neta_cv_eas])
-       AC_MSG_RESULT([         DDP enabled: $netatalk_cv_ddp_enabled])
        AC_MSG_RESULT([    CNID:])
        AC_MSG_RESULT([         backends: $compiled_backends])
        AC_MSG_RESULT([    UAMS:])
@@ -48,20 +47,22 @@ AC_DEFUN([AC_NETATALK_CONFIG_SUMMARY], [
        AC_MSG_RESULT([         passwd  ($uams_using_options)])
        AC_MSG_RESULT([         guest])
        AC_MSG_RESULT([    Options:])
-       AC_MSG_RESULT([         CUPS support:           $netatalk_cv_use_cups])
-       AC_MSG_RESULT([         SLP support:            $netatalk_cv_srvloc])
-       AC_MSG_RESULT([         tcp wrapper support:    $netatalk_cv_tcpwrap])
+       AC_MSG_RESULT([         DDP (AppleTalk) support: $netatalk_cv_ddp_enabled])
+       AC_MSG_RESULT([         CUPS support:            $netatalk_cv_use_cups])
+       AC_MSG_RESULT([         SLP support:             $netatalk_cv_srvloc])
+       AC_MSG_RESULT([         Zeroconf support:        $netatalk_cv_zeroconf])
+       AC_MSG_RESULT([         tcp wrapper support:     $netatalk_cv_tcpwrap])
 dnl    if test x"$netatalk_cv_linux_sendfile" != x; then
-dnl            AC_MSG_RESULT([         Linux sendfile support: $netatalk_cv_linux_sendfile])
+dnl            AC_MSG_RESULT([         Linux sendfile support:  $netatalk_cv_linux_sendfile])
 dnl    fi
-       AC_MSG_RESULT([         quota support:          $netatalk_cv_quotasupport])
-       AC_MSG_RESULT([         admin group support:    $netatalk_cv_admin_group])
-       AC_MSG_RESULT([         valid shell check:      $netatalk_cv_use_shellcheck])
-       AC_MSG_RESULT([         cracklib support:       $netatalk_cv_with_cracklib])
-       AC_MSG_RESULT([         dropbox kludge:         $netatalk_cv_dropkludge])
-       AC_MSG_RESULT([         force volume uid/gid:   $netatalk_cv_force_uidgid])
-       AC_MSG_RESULT([         Apple 2 boot support:   $compile_a2boot])
-       AC_MSG_RESULT([         ACL support:            $neta_cv_nfsv4acl])
+       AC_MSG_RESULT([         quota support:           $netatalk_cv_quotasupport])
+       AC_MSG_RESULT([         admin group support:     $netatalk_cv_admin_group])
+       AC_MSG_RESULT([         valid shell check:       $netatalk_cv_use_shellcheck])
+       AC_MSG_RESULT([         cracklib support:        $netatalk_cv_with_cracklib])
+       AC_MSG_RESULT([         dropbox kludge:          $netatalk_cv_dropkludge])
+       AC_MSG_RESULT([         force volume uid/gid:    $netatalk_cv_force_uidgid])
+       AC_MSG_RESULT([         Apple 2 boot support:    $compile_a2boot])
+       AC_MSG_RESULT([         ACL support:             $with_acl_support])
        if test x"$use_pam_so" = x"yes" -a x"$netatalk_cv_install_pam" = x"no"; then
                AC_MSG_RESULT([])
                AC_MSG_WARN([ PAM support was configured for your system, but the netatalk PAM configuration file])
diff --git a/macros/zeroconf.m4 b/macros/zeroconf.m4
new file mode 100644 (file)
index 0000000..788a193
--- /dev/null
@@ -0,0 +1,71 @@
+dnl Check for optional Zeroconf support
+
+AC_DEFUN([NETATALK_ZEROCONF], [
+       ZEROCONF_LIBS=""
+       ZEROCONF_CFLAGS=""
+       found_zeroconf=no
+       zeroconf_dir=""
+
+       AC_ARG_ENABLE(zeroconf,
+               [  --enable-zeroconf[[=DIR]]   enable Zeroconf support [[auto]]],
+               [zeroconf=$enableval],
+               [zeroconf=try]
+       )
+
+    dnl make sure atalk_libname is defined beforehand
+    [[ -n "$atalk_libname" ]] || AC_MSG_ERROR([internal error, atalk_libname undefined])
+
+       if test "x$zeroconf" != "xno"; then
+               savedcppflags="$CPPFLAGS"
+               savedldflags="$LDFLAGS"
+
+               if test "x$zeroconf" = "xyes" -o "x$zeroconf" = "xtry"; then
+                       zeroconf_dir="/usr"
+               else
+                       zeroconf_dir="$zeroconf"
+               fi
+
+    # mDNS support using Avahi
+    AC_CHECK_HEADER(
+        avahi-client/client.h,
+        AC_CHECK_LIB(
+           avahi-client,
+           avahi_client_new,
+           AC_DEFINE(USE_ZEROCONF, 1, [Use DNS-SD registration]))
+    )
+
+    case "$ac_cv_lib_avahi_client_avahi_client_new" in
+      yes)
+      PKG_CHECK_MODULES(AVAHI, [ avahi-client >= 0.6 ])
+      PKG_CHECK_MODULES(AVAHI_TPOLL, [ avahi-client >= 0.6.4 ],
+        [AC_DEFINE(HAVE_AVAHI_THREADED_POLL, 1, [Uses Avahis threaded poll implementation])],
+        [AC_MSG_WARN(This Avahi implementation is not supporting threaded poll objects. Maybe this is not what you want.)])
+      ZEROCONF_LIBS="$AVAHI_LIBS"
+      ZEROCONF_CFLAGS="$AVAHI_CFLAGS"
+      AC_DEFINE(HAVE_AVAHI, 1, [Use Avahi/DNS-SD registration])
+      found_zeroconf=yes
+      ;;
+    esac
+
+               CPPFLAGS="$savedcppflags"
+               LDFLAGS="$savedldflags"
+       fi
+       
+       netatalk_cv_zeroconf=no
+       AC_MSG_CHECKING([whether to enable Zerconf support])
+       if test "x$found_zeroconf" = "xyes"; then
+               AC_MSG_RESULT([yes])
+               AC_DEFINE(USE_ZEROCONF, 1, [Define to enable Zeroconf support])
+               netatalk_cv_zeroconf=yes
+       else
+               AC_MSG_RESULT([no])
+               if test "x$zeroconf" != "xno" -a "x$zeroconf" != "xtry"; then
+                       AC_MSG_ERROR([Zeroconf installation not found])
+               fi
+       fi
+
+       LIB_REMOVE_USR_LIB(ZEROCONF_LIBS)
+       CFLAGS_REMOVE_USR_INCLUDE(ZEROCONF_CFLAGS)
+       AC_SUBST(ZEROCONF_LIBS)
+       AC_SUBST(ZEROCONF_CFLAGS)
+])
index b646244e5cc4aa571b7b480dd1b8e113376ced95..c767a967abd49b2fcfcba5f0d80066eb03c9b2a1 100644 (file)
@@ -5,6 +5,7 @@ apple_dump.1
 apple_mv.1
 apple_rm.1
 asip-status.pl.1
+afpldaptest.1
 uniconv.1
 .gitignore
 *.o
index a9e39c1529d58bf570aeaad927bf3709c5631fe5..0154fc327a6a8686cc669b0e74d2c0f05649001c 100644 (file)
@@ -12,35 +12,34 @@ SUFFIXES= .tmpl .
            -e s@:DEFAULT_CNID_SCHEME:@${DEFAULT_CNID_SCHEME}@ \
            <$< >$@
 
-GENERATED_MANS = apple_cp.1 apple_mv.1 apple_rm.1 uniconv.1 asip-status.pl.1
-TEMPLATE_FILES = apple_cp.1.tmpl apple_mv.1.tmpl apple_rm.1.tmpl uniconv.1.tmpl asip-status.pl.1.tmpl
-NONGENERATED_MANS      =       achfile.1  \
-                               ad.1 \
-                               aecho.1 \
-                               afile.1 \
+GENERATED_MANS = uniconv.1 asip-status.pl.1 afpldaptest.1
+TEMPLATE_FILES = uniconv.1.tmpl asip-status.pl.1.tmpl afpldaptest.1.tmpl
+NONGENERATED_MANS      =       ad.1 \
                                afppasswd.1 \
                                apple_dump.1 \
                                dbd.1 \
-                               getzones.1 \
                                hqx2bin.1 \
                                macbinary.1 \
                                megatron.1 \
+                               netatalk-config.1 \
+                               single2bin.1 \
+                               unbin.1 \
+                               unhex.1 \
+                               unsingle.1
+ATALK_MANS = aecho.1 \
+                               getzones.1 \
                                nbp.1 \
                                nbplkup.1 \
                                nbprgstr.1 \
                                nbpunrgstr.1 \
-                               netatalk-config.1 \
                                pap.1 \
                                papstatus.1 \
-                               psorder.1 \
-                               single2bin.1 \
-                               unbin.1 \
-                               unhex.1 \
-                               unsingle.1
+                               psorder.1
 
-man_MANS = $(GENERATED_MANS) $(NONGENERATED_MANS)
+if USE_APPLETALK
+NONGENERATED_MANS += $(ATALK_MANS)
+endif
 
+man_MANS = $(GENERATED_MANS) $(NONGENERATED_MANS)
 CLEANFILES = $(GENERATED_MANS)
-
-EXTRA_DIST = $(TEMPLATE_FILES) $(NONGENERATED_MANS)
-
+EXTRA_DIST = $(TEMPLATE_FILES) $(NONGENERATED_MANS) $(ATALK_MANS)
diff --git a/man/man1/achfile.1 b/man/man1/achfile.1
deleted file mode 100644 (file)
index 3acef04..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-'\" t
-.\"     Title: achfile
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 26 Feb 1998
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
-.\"  Language: English
-.\"
-.TH "ACHFILE" "1" "26 Feb 1998" "Netatalk 2.1" "Netatalk 2.1"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-achfile \- change type and/or creator of Apple Macintosh files (netatalk format)
-.SH "SYNOPSIS"
-.HP \w'\fBachfile\fR\fB\fR\ 'u
-\fBachfile\fR\fB\fR [\-t\ \fItype\fR] [\-c\ \fIcreator\fR] \fIfile\fR...
-.SH "DESCRIPTION"
-.PP
-\fBachfile\fR
-changes the Macintosh type and/or creator of the
-\fIfile\fR
-arguments which have a corresponding \&.AppleDouble file\&.
-.SH "OPTIONS"
-.PP
-\fB\-t\fR
-\fItype\fR
-change the type\&.
-.PP
-\fB\-c\fR
-\fIcreator\fR
-change the creator\&.
-.SH "DIAGNOSTICS"
-.PP
-returns exit status 0 if all files changed successfully
-.SH "SEE ALSO"
-.PP
-\fBafile\fR(1),
-\fBafpd\fR(8)
index 597b4a4e5ef190f2859b7b402582c13cdebef5db..b42b79b1a2b063b8b43350dac4daf1ba3de19225 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: ad
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 01 Sep 2009
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"      Date: 30 Mar 2011
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "AD" "1" "01 Sep 2009" "Netatalk 2.1" "Netatalk 2.1"
+.TH "AD" "1" "30 Mar 2011" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -22,7 +22,7 @@
 ad \- Netatalk compatible UNIX file utility suite\&.
 .SH "SYNOPSIS"
 .HP \w'\fBad\fR\ 'u
-\fBad\fR {ls} [\&.\&.\&.]
+\fBad\fR {ls\ |\ cp\ |\ mv\ |\ rm} [\&.\&.\&.]
 .SH "DESCRIPTION"
 .PP
 \fBad\fR
@@ -31,46 +31,208 @@ files in
 \&.AppleDouble
 directories and the CNID databases are updated as appropiate\&.
 .SH "AVAILABLE COMMANDS"
+.HP \w'\fBad\ ls\fR\ 'u
+\fBad ls\fR [\-dRl\ [u]] {file|dir\ [\&.\&.\&.]}
 .PP
-.HP \w'\fBad\ ls\fR\ 'u \fBad ls\fR [\-dRl\ [u]] {file|dir\ [\&.\&.\&.]}
+List files and directories\&.
+.HP \w'\fBad\ cp\fR\ 'u
+\fBad cp\fR [\-aipvf] {src_file} {dst_file}
+.HP \w'\fBad\ cp\ \-R\fR\ 'u
+\fBad cp \-R\fR [\-aipvf] {src_file|src_directory\ \&.\&.\&.} {dst_directory}
+.PP
+Copy files and directories\&.
+.HP \w'\fBad\ mv\fR\ 'u
+\fBad mv\fR [\-finv] {src_file} {dst_file}
+.HP \w'\fBad\ mv\fR\ 'u
+\fBad mv\fR [\-finv] {src_file|src_directory\ \&.\&.\&.} {dst_directory}
+.PP
+Move files and directories\&.
+.HP \w'\fBad\ rm\fR\ 'u
+\fBad rm\fR [\-Rv] {file|directory}
+.PP
+Remove files and directories\&.
+.SH "AD LS"
+.PP
+List files and directories\&. Options:
+.PP
+\-d
+.RS 4
+Directories are listed as plain files
+.RE
+.PP
+\-R
+.RS 4
+list subdirectories recursively
+.RE
+.PP
+\-l
+.RS 4
+Long output, list AFP info
+.RE
+.PP
+\-u
+.RS 4
+List UNIX info
+.RE
+.PP
+\fILong output description\fR
 .sp
 .if n \{\
 .RS 4
 .\}
 .nf
-\-l Long Output [\-u: unix info]:
+<unixinfo> <FinderFlags> <AFP Attributes> <Color> <Type> <Creator> <CNID from AppleDouble> <name>
+
+FinderFlags (valid for (f)iles and/or (d)irectories):
 
-   <unixinfo \&.\&.\&.> <FinderFlags> <AFPAttributes> <Color> <Type> <Creator> <CNID from AppleDouble> <name>
+  d = On Desktop                      (f/d)
+  e = Hidden extension                (f/d)
+  m = Shared (can run multiple times) (f)
+  n = No INIT resources               (f)
+  i = Inited                          (f/d)
+  c = Custom icon                     (f/d)
+  t = Stationery                      (f)
+  s = Name locked                     (f/d)
+  b = Bundle                          (f/d)
+  v = Invisible                       (f/d)
+  a = Alias file                      (f/d)
 
-   FinderFlags (valid for (f)ile and/or (d)irectory):
-     d = On Desktop (f/d)
-     e = Hidden extension (f/d)
-     m = Shared (can run multiple times) (f)
-     n = No INIT resources (f)
-     i = Inited (f/d)
-     c = Custom icon (f/d)
-     t = Stationery (f)
-     s = Name locked (f/d)
-     b = Bundle (f/d)
-     v = Invisible (f/d)
-     a = Alias file (f/d)
+AFP Attributes:
 
-   AFPAttributes:
-     y = System (f/d)
-     w = No write (f)
-     p = Needs backup (f/d)
-     r = No rename (f/d)
-     l = No delete (f/d)
-     o = No copy (f)
+  y = System                          (f/d)
+  w = No write                        (f)
+  p = Needs backup                    (f/d)
+  r = No rename                       (f/d)
+  l = No delete                       (f/d)
+  o = No copy                         (f)
 
-  Note: any letter appearing in uppercase means the flag is set but it\'s a directory for which the flag is not allowed\&.
+Note: any letter appearing in uppercase means the flag is set but it\'s a directory for which the flag is not allowed\&.
 .fi
 .if n \{\
 .RE
 .\}
+.SH "AD CP"
+.PP
+Copy files and directories\&.
+.PP
+In the first synopsis form, the cp utility copies the contents of the source_file to the target_file\&. In the second synopsis form, the contents of each named source_file is copied to the destination target_directory\&. The names of the files themselves are not changed\&. If cp detects an attempt to copy a file to itself, the copy will fail\&.
+.PP
+Netatalk AFP volumes are detected by means of their "\&.AppleDesktop" directory which is located in their volume root\&. When a copy targetting an AFP volume is detected, its CNID database daemon is connected and all copies will also go through the CNID database\&. AppleDouble files are also copied and created as needed when the target is an AFP volume\&.
+.PP
+Options:
+.PP
+\-a
+.RS 4
+Archive mode\&. Same as \-Rp\&.
+.RE
+.PP
+\-f
+.RS 4
+For each existing destination pathname, remove it and create a new file, without prompting for confirmation regardless of its permis\- sions\&. (The \-f option overrides any previous \-i or \-n options\&.)
+.RE
+.PP
+\-i
+.RS 4
+Cause cp to write a prompt to the standard error output before copying a file that would overwrite an existing file\&. If the response from the standard input begins with the character \'y\' or \'Y\', the file copy is attempted\&. (The \-i option overrides any pre\- vious \-f or \-n options\&.)
+.RE
+.PP
+\-n
+.RS 4
+Do not overwrite an existing file\&. (The \-n option overrides any previous \-f or \-i options\&.)
+.RE
+.PP
+\-p
+.RS 4
+Cause cp to preserve the following attributes of each source file in the copy: modification time, access time, file flags, file mode, user ID, and group ID, as allowed by permissions\&. If the user ID and group ID cannot be preserved, no error message is displayed and the exit value is not altered\&.
+.RE
+.PP
+\-R
+.RS 4
+If source_file designates a directory, cp copies the directory and the entire subtree connected at that point\&.If the source_file ends in a /, the contents of the directory are copied rather than the directory itself\&.
+.RE
+.PP
+\-v
+.RS 4
+Cause cp to be verbose, showing files as they are copied\&.
+.RE
+.PP
+\-x
+.RS 4
+File system mount points are not traversed\&.
+.RE
+.SH "AD MV"
+.PP
+Move files and directories\&.
+.PP
+Move files around within an AFP volume, updating the CNID database as needed\&. If either:
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+source or destination is not an AFP volume
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+.sp -1
+.IP \(bu 2.3
+.\}
+source AFP volume != destination AFP volume
+.RE
+.sp
+.RE
+the files are copied and removed from the source\&.
+.PP
+Options:
+.PP
+\-f
+.RS 4
+Do not prompt for confirmation before overwriting the destination path\&. (The \-f option overrides any previous \-i or \-n options\&.)
+.RE
+.PP
+\-i
+.RS 4
+Cause mv to write a prompt to standard error before moving a file that would overwrite an existing file\&. If the response from the standard input begins with the character `y\' or `Y\', the move is attempted\&. (The \-i option overrides any previous \-f or \-n options\&.)
+.RE
+.PP
+\-n
+.RS 4
+Do not overwrite an existing file\&. (The \-n option overrides any previous \-f or \-i options\&.)
+.RE
+.PP
+\-v
+.RS 4
+Cause mv to be verbose, showing files after they are moved\&.
+.RE
+.SH "AD RM"
+.PP
+Remove files and directories\&.
+.PP
+The rm utility attempts to remove the non\-directory type files specified on the command line\&. If the files and directories reside on an AFP volume, the corresponding CNIDs are deleted from the volumes database\&.
+.PP
+The options are as follows:
+.PP
+\-R
+.RS 4
+Attempt to remove the file hierarchy rooted in each file argument\&.
+.RE
+.PP
+\-v
+.RS 4
+Be verbose when deleting files, showing them as they are removed\&.
+.RE
 .SH "REPORTING BUGS"
 .PP
 Report bugs to the Netatalk\-devel list <netatalk\-devel@lists\&.sourceforge\&.net>\&.
 .SH "SEE ALSO"
 .PP
-\fBdbd\fR(1)\&.
+\fBdbd\fR(1),
+\fBapple_dump\fR(1)\&.
index 24e108aedcac433fbb5065f1678a11627fb0f158..3dfad5fe5941474784795afb805afd30ba4b721a 100644 (file)
@@ -3,11 +3,11 @@
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: 17 Dec 1991
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "AECHO" "1" "17 Dec 1991" "Netatalk 2.1" "Netatalk 2.1"
+.TH "AECHO" "1" "17 Dec 1991" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
diff --git a/man/man1/afpldaptest.1.tmpl b/man/man1/afpldaptest.1.tmpl
new file mode 100644 (file)
index 0000000..2ea753c
--- /dev/null
@@ -0,0 +1,58 @@
+'\" t
+.\"     Title: afpldaptest
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
+.\"      Date: 30 Mar 2011
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
+.\"  Language: English
+.\"
+.TH "AFPLDAPTEST" "1" "30 Mar 2011" "Netatalk 2.2" "Netatalk 2.2"
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+afpldaptest \- Syntactically check an afp_ldap\&.conf
+.SH "SYNOPSIS"
+.HP \w'\fBafpldaptest\fR\fB\fR\fBafpldaptest\fR\fB\fR\ 'u
+\fBafpldaptest\fR\fB\fR {\-u\ \fIUSER\fR | \-g\ \fIGROUP\fR | \-i\ \fIUUID\fR}
+.br
+\fBafpldaptest\fR\fB\fR {\-h | \-? | \-:}
+.SH "DESCRIPTION"
+.PP
+\fBafpldaptest\fR
+is a simple command to syntactically check :ETCDIR:/afp_ldap\&.conf\&.
+.SH "OPTIONS"
+.PP
+\fB\-u\fR \fIUSER\fR
+.RS 4
+Show uuid for
+\fIUSER\fR\&.
+.RE
+.PP
+\fB\-g\fR \fIGROUP\fR
+.RS 4
+Show uuid for
+\fIGROUP\fR\&.
+.RE
+.PP
+\fB\-i\fR \fIUUID\fR
+.RS 4
+Show user, group or local\-uuid for
+\fIUUID\fR\&.
+.RE
+.PP
+\fB\-h, \-?, \-:\fR
+.RS 4
+Show the help and exit\&.
+.RE
+.SH "SEE ALSO"
+.PP
+\fBafp_ldap.conf\fR(5)
index 0fea6807c4a7c5692da2ec65e0760767a7388e91..8d72f85c5477fdae3cabb6584fd0c55ebd59077a 100644 (file)
@@ -3,11 +3,11 @@
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: 22 Aug 2004
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "AFPPASSWD" "1" "22 Aug 2004" "Netatalk 2.1" "Netatalk 2.1"
+.TH "AFPPASSWD" "1" "22 Aug 2004" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
diff --git a/man/man1/apple_cp.1.tmpl b/man/man1/apple_cp.1.tmpl
deleted file mode 100644 (file)
index 59dd84b..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-'\" t
-.\"     Title: apple_cp
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 22 Aug 2004
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
-.\"  Language: English
-.\"
-.TH "APPLE_CP" "1" "22 Aug 2004" "Netatalk 2.1" "Netatalk 2.1"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-apple_cp \- Do an apple copy, copying file metadata and the resource fork as well
-.SH "SYNOPSIS"
-.PP
-\fB:BINDIR:/apple_cp\fR\fB\fR
-\fISOURCE DEST\fR
-\fB:BINDIR:/apple_cp\fR
-\fISOURCE\fR\&.\&.\&.
-\fIDIRECTORY\fR
-.SH "DESCRIPTION"
-.PP
-\fBapple_cp\fR
-is a perl script to copy SOURCE to DEST or multiple SOURCE(s) to DIRECTORY\&. It also copies the file specific metadata (including resource forks) to the \&.AppleDouble directory for DEST or DIRECTORY\&. If the \&.AppleDouble directory doesn\'t exist for DEST or DIRECTORY it will create it\&.
-.SH "EXAMPLES"
-.PP
-\fB:BINDIR:/apple_cp\fR
-test\&.text /target/directory/
-.PP
-\fB:BINDIR:/apple_cp\fR
-test\&.text /target/directory/test2\&.text
-.PP
-\fB:BINDIR:/apple_cp\fR
-test\&.text testing\&.text /target/directory/
-.SH "REPORTING BUGS"
-.PP
-Report bugs to the Netatalk\-devel list <netatalk\-devel@lists\&.sourceforge\&.net>\&.
-.SH "SEE ALSO"
-.PP
-\fBapple_mv\fR(1),
-\fBapple_rm\fR(1)\&.
index d915cd8cdfe43db2884537e0f2068587e189225c..7df7c0998e78149223351b869f213a6948d94002 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: apple_dump
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 31 Mar 2010
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"      Date: 30 Mar 2011
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "APPLE_DUMP" "1" "31 Mar 2010" "Netatalk 2.1" "Netatalk 2.1"
+.TH "APPLE_DUMP" "1" "30 Mar 2011" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -74,4 +74,4 @@ There is no way to detect whether FinderInfo is FileInfo or DirInfo\&. By defaul
 If setting option \-f or \-d, assume FinderInfo and doesn\'t look for another file\&.
 .SH "SEE ALSO"
 .PP
-\fBafile\fR(1)
+\fBad\fR(1)
diff --git a/man/man1/apple_mv.1.tmpl b/man/man1/apple_mv.1.tmpl
deleted file mode 100644 (file)
index e91d28c..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-'\" t
-.\"     Title: apple_mv
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 22 Aug 2004
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
-.\"  Language: English
-.\"
-.TH "APPLE_MV" "1" "22 Aug 2004" "Netatalk 2.1" "Netatalk 2.1"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-apple_mv \- Do an apple move, moving metadata and the resource fork as well
-.SH "SYNOPSIS"
-.PP
-\fB:BINDIR:/apple_mv\fR\fB\fR
-\fISOURCE DEST\fR
-\fB:BINDIR:/apple_mv\fR
-\fISOURCE\fR\&.\&.\&.
-\fIDIRECTORY\fR
-.SH "DESCRIPTION"
-.PP
-\fBapple_mv\fR
-is a perl script to move SOURCE to DEST or multiple SOURCE(s) to DIRECTORY\&. It also moves the file specific metadata (including resource forks) to the \&.AppleDouble directory for DEST or DIRECTORY\&. If the \&.AppleDouble directory doesn\'t exist for DEST or DIRECTORY it will create it\&.
-.SH "EXAMPLES"
-.PP
-\fB:BINDIR:/apple_mv\fR
-test\&.text /target/directory/
-.PP
-\fB:BINDIR:/apple_mv\fR
-test\&.text /target/directory/test2\&.text
-.PP
-\fB:BINDIR:/apple_mv\fR
-test\&.text testing\&.text /target/directory/
-.SH "REPORTING BUGS"
-.PP
-Report bugs to the Netatalk\-devel list <netatalk\-devel@lists\&.sourceforge\&.net>\&.
-.SH "SEE ALSO"
-.PP
-\fBapple_cp\fR(1),
-\fBapple_rm\fR(1)\&.
diff --git a/man/man1/apple_rm.1.tmpl b/man/man1/apple_rm.1.tmpl
deleted file mode 100644 (file)
index 90dc0a9..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-'\" t
-.\"     Title: apple_rm
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 22 Aug 2004
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
-.\"  Language: English
-.\"
-.TH "APPLE_RM" "1" "22 Aug 2004" "Netatalk 2.1" "Netatalk 2.1"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-apple_rm \- Do an apple remove, remove metadata and resource fork as well
-.SH "SYNOPSIS"
-.PP
-\fB:BINDIR:/apple_rm\fR\fB\fR
-\fIFILE\fR\&.\&.\&.
-.SH "DESCRIPTION"
-.PP
-\fBapple_rm\fR
-is a perl script that removes FILE(s) as well as the \&.AppleDouble metadata file(s) that corresponds to FILE(s)\&. These AppleDouble header files eventually also contain the resource fork if the files had one\&.
-\fBapple_rm\fR
-does not delete directories\&.
-.SH "EXAMPLES"
-.PP
-\fB:BINDIR:/apple_rm\fR
-test\&.text
-.PP
-\fB:BINDIR:/apple_rm\fR
-test\&.text testing\&.text
-.SH "REPORTING BUGS"
-.PP
-Report bugs to the Netatalk\-devel list <netatalk\-devel@lists\&.sourceforge\&.net>\&.
-.SH "SEE ALSO"
-.PP
-\fBapple_cp\fR(1),
-\fBapple_mv\fR(1)\&.
index f5c2d64afcd35a286af6a1774819a3a2104dbc31..2bf64bdc7ddb3bc111e5bd5f106e6e364e7524b3 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: asip-status.pl
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 22 Aug 2004
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"      Date: 10 Mar 2011
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "ASIP\-STATUS\&.PL" "1" "22 Aug 2004" "Netatalk 2.1" "Netatalk 2.1"
+.TH "ASIP\-STATUS\&.PL" "1" "10 Mar 2011" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
 .SH "NAME"
 asip-status.pl \- Queries AFP servers for their capabilities
 .SH "SYNOPSIS"
-.PP
-\fB:BINDIR:/asip\-status\&.pl\fR\fB\fR
-ADDRESS\fI:PORT\fR\&.\&.\&.
+.HP \w'\fBasip\-status\&.pl\fR\fB\fR\ 'u
+\fBasip\-status\&.pl\fR\fB\fR [\-d] [\-i] [\-x] HOSTNAME[:PORT]
 .SH "DESCRIPTION"
 .PP
 \fBasip\-status\&.pl\fR
-is a perl script that sends a FPGetSrvrInfo request to an AFP server at ADDRESS:PORT and displays the results, namely "Machine type", the server\'s name, supported AFP versions, UAMs and AFP flags, the "server signature" and the network addresses, the server provides AFP services on\&.
+is a perl script that sends a FPGetSrvrInfo request to an AFP server at HOSTNAME:PORT and displays the results, namely "Machine type", the server\'s name, supported AFP versions, UAMs and AFP flags, the "server signature" and the network addresses, the server provides AFP services on\&.
 .PP
 When you don\'t supply :PORT, then the default AFP port, 548, will be used\&.
+.SH "OPTIONS"
+.PP
+\fB\-d\fR
+.RS 4
+Enable debug output\&.
+.RE
+.PP
+\fB\-i\fR
+.RS 4
+Show icon if it exists\&.
+.RE
+.PP
+\fB\-x\fR
+.RS 4
+Enable hex dump output\&.
+.RE
 .SH "EXAMPLES"
 .PP
 .if n \{\
 .RS 4
 .\}
 .nf
-\fB:BINDIR:/asip\-status\&.pl\fR 192\&.168\&.21\&.2
+\fBasip\-status\&.pl\fR 192\&.168\&.21\&.2
 AFP reply from 192\&.168\&.21\&.2:548
 Flags: 1  Cmd: 3  ID: 57005
 Reply: DSIGetStatus
@@ -61,8 +76,8 @@ Network address: 10\&.20 (ddp address)
 .RS 4
 .\}
 .nf
-\fB:BINDIR:/asip\-status\&.pl\fR 192\&.168\&.21\&.1:10548
-AFP reply from 192\&.168\&.21\&.1:10548
+\fBasip\-status\&.pl\fR myserver:10548
+AFP reply from myserver:10548
 Flags: 1  Cmd: 3  ID: 57005
 Reply: DSIGetStatus
 Request ID: 57005
index 54d40ee6f2a20866c76936f10754f7354b9db869..caff254846f426d85fd1f3505ece5c5265afcd2c 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: dbd
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 23 Dec 2009
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"      Date: 12 Oct 2010
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "DBD" "1" "23 Dec 2009" "Netatalk 2.1" "Netatalk 2.1"
+.TH "DBD" "1" "12 Oct 2010" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -135,7 +135,6 @@ Options:
 .RS 4
 \fB\-c\fR
 Don\'t check \&.AppleDouble stuff, only check orphaned\&.
-
 \fB\-n\fR
 Don\'t open CNID database, skip CNID checks, only traverse filesystem
 .RE
index c60148629b1064febac5bb9bb093aacf031ca0ec..861ca78384ca08297e4b01a4fbddfb85cd24782d 100644 (file)
@@ -3,11 +3,11 @@
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: 17 Dec 1991
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "GETZONES" "1" "17 Dec 1991" "Netatalk 2.1" "Netatalk 2.1"
+.TH "GETZONES" "1" "17 Dec 1991" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
index b665673ec0ee3cffd67cfd2fc71c7b2c27c287c6..7b55c76fee304c12dbde5e500ef3ca3135eee5b1 100644 (file)
@@ -3,11 +3,11 @@
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: 8 Jan 1992
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "MEGATRON" "1" "8 Jan 1992" "Netatalk 2.1" "Netatalk 2.1"
+.TH "MEGATRON" "1" "8 Jan 1992" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
index 036cd31886a6a6f8bb6a04493efffa00db3341c0..0958b9973c23e69778c6b64417a547b070349a0a 100644 (file)
@@ -3,11 +3,11 @@
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: 24 June 2004
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "NBP" "1" "24 June 2004" "Netatalk 2.1" "Netatalk 2.1"
+.TH "NBP" "1" "24 June 2004" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
index 3ceecbcefefc6cfdb99b2354ebbad4a35b39fe13..3d4d9a8b8905b7a53f8485e4ecb35a76b635b1dc 100644 (file)
@@ -4,10 +4,10 @@
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: 09 June 2001
 .\"    Manual: The Netatalk Project
-.\"    Source: Netatalk 2.1
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "NETATALK\-CONFIG" "1" "09 June 2001" "Netatalk 2.1" "The Netatalk Project"
+.TH "NETATALK\-CONFIG" "1" "09 June 2001" "Netatalk 2.2" "The Netatalk Project"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
index 784ae4b7151c8ca6623f46783058988f7c1c6c15..bc333287c3cb5d563eee7dcbe5d6d5850b1d4d71 100644 (file)
@@ -3,11 +3,11 @@
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: 6 May 2002
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "PAP" "1" "6 May 2002" "Netatalk 2.1" "Netatalk 2.1"
+.TH "PAP" "1" "6 May 2002" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
index 9f736cb0eacc10d0f9bf031502b75c6a2206cf40..724fe555b14bc651da1093937c732a039cc8daad 100644 (file)
@@ -3,11 +3,11 @@
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: 17 Dec 1991
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "PSORDER" "1" "17 Dec 1991" "Netatalk 2.1" "Netatalk 2.1"
+.TH "PSORDER" "1" "17 Dec 1991" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
index 1d49c30b170a21a0e18d942e97468002f7d601d9..9e314b52749147e0509c41281a0b75724d156bc9 100644 (file)
@@ -3,11 +3,11 @@
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: 24 Jun 2004
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "UNICONV" "1" "24 Jun 2004" "Netatalk 2.1" "Netatalk 2.1"
+.TH "UNICONV" "1" "24 Jun 2004" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
index 1569cfd7296d6eb254f24544583996bdda7d86d4..198f977243df36fd0afc681afe6765816622ea65 100644 (file)
@@ -1,5 +1,9 @@
 # Makefile.am for man/man3
 
-man_MANS = atalk_aton.3 nbp_name.3
+ATALK_MANS = atalk_aton.3 nbp_name.3
 
-EXTRA_DIST = $(man_MANS)
+if USE_APPLETALK
+man_MANS = $(ATALK_MANS)
+endif
+
+EXTRA_DIST = $(ATALK_MANS)
index f8420ead7bd0f691f9497af040d2a9e13e0fd477..d4f5d94f5069cfe5e96ea18c66bba0e7ca394e09 100644 (file)
@@ -3,11 +3,11 @@
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: 12 Jan 1994
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "ATALK_ATON" "3" "12 Jan 1994" "Netatalk 2.1" "Netatalk 2.1"
+.TH "ATALK_ATON" "3" "12 Jan 1994" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
index 4a9e1026d877766e56564d5f271b44a8ebb6e71b..6791f4abc9de4755b988759bba99458a897ab13c 100644 (file)
@@ -3,11 +3,11 @@
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: 12 Jan 1994
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "NBP_NAME" "3" "12 Jan 1994" "Netatalk 2.1" "Netatalk 2.1"
+.TH "NBP_NAME" "3" "12 Jan 1994" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
index 944c3602ab2cfc1dad0e2b09bb738e9117da0e09..d0295d419af17c6cbdd335f6cadcfad0781ddb5b 100644 (file)
@@ -1,5 +1,9 @@
 # Makefile.am for man/man4/
 
-man_MANS = atalk.4
+ATALK_MANS = atalk.4
 
-EXTRA_DIST = $(man_MANS)
+if USE_APPLETALK
+man_MANS = $(ATALK_MANS)
+endif
+
+EXTRA_DIST = $(ATALK_MANS)
index 24be709eaf66244a1d73e14c2afe35d3949b1cc9..356b93e7486669d7d2ab429a6c93c7ebce848d47 100644 (file)
@@ -3,11 +3,11 @@
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: 17 Dec 1991
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "ATALK" "4" "17 Dec 1991" "Netatalk 2.1" "Netatalk 2.1"
+.TH "ATALK" "4" "17 Dec 1991" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
index 9b3914ed779550d5ab052c377ecfe25400117ab9..c6a1255bb7908c0ffd6885c5914dd2807ab4f7a8 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: AppleVolumes.default
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 22 Apr 2010
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"      Date: 30 Mar 2011
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "APPLEVOLUMES\&.DEFAU" "5" "22 Apr 2010" "Netatalk 2.1" "Netatalk 2.1"
+.TH "APPLEVOLUMES\&.DEFAU" "5" "30 Mar 2011" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -234,11 +234,9 @@ options:\fI[option]\fR
 .RS 4
 This allows multiple options to be specified in a comma delimited format\&. The available options are:
 .PP
-acls
+searchdb
 .RS 4
-Enable ACLs on this volume\&. Requires a
-\fINFSv4 ACLs\fR
-compatible filesystem (e\&.g\&. ZFS) and an ACL API compatible to *Solaris\&. In other words: this requires Solaris, Opensolaris or a derived distribution\&.
+Use fast CNID database namesearch instead of slow recursive filesystem search\&. Relies on a consistent CNID database, ie Samba or local filesystem access lead to inaccurate or wrong results\&. Works only for "dbd" CNID db volumes\&.
 .RE
 .PP
 tm
@@ -355,11 +353,9 @@ rwlist:\fI[users/groups]\fR
 Allows certain users and groups to have read/write access to a share\&. This follows the allow option format\&.
 .RE
 .PP
-veto:\fI[vetoed name]\fR
+veto:\fI[vetoed names]\fR
 .RS 4
-hide files and directories,where the path matches one of the \'/\' delimited vetoed names\&. Matches are partial, e\&.g\&. path is
-/abc/def/file
-and veto:/abc/ will hide the file\&.
+hide files and directories,where the path matches one of the \'/\' delimited vetoed names\&. The veto string must always be terminated with a \'/\', eg\&. "veto1/", "veto1/veto2/"\&.
 .RE
 .PP
 volcharset:\fI[charset]\fR
@@ -726,5 +722,4 @@ Provides compatibility with Apple II clients\&. (legacy)
 .PP
 \fBafpd.conf\fR(5),
 \fBafpd\fR(8),
-\fBafp_ldap.conf\fR(5),
-\fBafp_acls\fR(8)
+\fBcnid_metad\fR(8)
index a1e28886df45aa60cca4ce65529e79ca207da133..a382039eeae6ebfa7475aba6bb89d57ab5039139 100644 (file)
@@ -13,13 +13,26 @@ SUFFIXES = .tmpl .
            -e "s@:COMPILED_BACKENDS:@${compiled_backends}@g" \
            <$< >$@
 
-GENERATED_MANS = AppleVolumes.default.5 afpd.conf.5 \
-       atalkd.conf.5 netatalk.conf.5 papd.conf.5 \
-       afp_ldap.conf.5 afp_signature.conf.5
-
-TEMPLATE_FILES = AppleVolumes.default.5.tmpl afpd.conf.5.tmpl \
-       atalkd.conf.5.tmpl netatalk.conf.5.tmpl papd.conf.5.tmpl \
-       afp_ldap.conf.5.tmpl afp_signature.conf.5.tmpl
+GENERATED_MANS = AppleVolumes.default.5 \
+       afpd.conf.5 \
+       netatalk.conf.5 \
+       afp_ldap.conf.5 \
+       afp_signature.conf.5 \
+       afp_voluuid.conf.5
+
+TEMPLATE_FILES = AppleVolumes.default.5.tmpl \
+       afpd.conf.5.tmpl \
+       netatalk.conf.5.tmpl \
+       afp_ldap.conf.5.tmpl \
+       afp_signature.conf.5.tmpl \
+       afp_voluuid.conf.5.tmpl
+
+ATALK_MANS = atalkd.conf.5.tmpl papd.conf.5.tmpl
+
+if USE_APPLETALK
+GENERATED_MANS += atalkd.conf.5 papd.conf.5
+TEMPLATE_FILES += $(ATALK_MANS)
+endif
 
 NONGENERATED_MANS = AppleVolumes.5 AppleVolumes.system.5
 
@@ -27,4 +40,4 @@ man_MANS = $(GENERATED_MANS) $(NONGENERATED_MANS)
 
 CLEANFILES = $(GENERATED_MANS)
 
-EXTRA_DIST = $(TEMPLATE_FILES) $(NONGENERATED_MANS)
+EXTRA_DIST = $(TEMPLATE_FILES) $(NONGENERATED_MANS) $(ATALK_MANS)
index 0b909e330338867f9df634309bd7ad12adad6539..8ad13027b1a48aa084bab7b1f9253823f503d66f 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: afp_ldap.conf
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 28 November 2009
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"      Date: 30 Mar 2011
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "AFP_LDAP\&.CONF" "5" "28 November 2009" "Netatalk 2.1" "Netatalk 2.1"
+.TH "AFP_LDAP\&.CONF" "5" "30 Mar 2011" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -160,4 +160,5 @@ ldap_group_attr  = cn
 .SH "SEE ALSO"
 .PP
 \fBafpd\fR(8),
-\fBAppleVolumes.default\fR(5),\fBafpldaptest\fR(1)
+\fBAppleVolumes.default\fR(5),
+\fBafpldaptest\fR(1)
index 59de330138122c603f06814f902e7bfc32a97ed0..592b8fbb0171c2e3b54057a85ff37e5d3168a44f 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: afp_signature.conf
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 29 March 2010
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"      Date: 08 Mar 2011
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "AFP_SIGNATURE\&.CONF" "5" "29 March 2010" "Netatalk 2.1" "Netatalk 2.1"
+.TH "AFP_SIGNATURE\&.CONF" "5" "08 Mar 2011" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -25,7 +25,7 @@ afp_signature.conf \- Configuration file used by afpd(8) to specify server signa
 :ETCDIR:/afp_signature\&.conf
 is the configuration file used by
 \fBafpd\fR
-to specify server signature automatically\&. The configuration lines are composed like:
+to specify server signature automagically\&. The configuration lines are composed like:
 .PP
 \fI"server name"\fR
 \fIhexa\-string\fR
@@ -50,7 +50,7 @@ Server Signature is unique 16\-bytes identifier used to prevent logging on to th
 .PP
 Netatalk 2\&.0 and earlier generated server signature by using gethostid()\&. There was a problem that another servers have the same signature because the hostid is not unique enough\&.
 .PP
-Netatalk 2\&.1 generates the signature from random numbers and saves it into afp_signature\&.conf\&. When starting next time, it is read from this file\&.
+Current netatalk generates the signature from random numbers and saves it into afp_signature\&.conf\&. When starting next time, it is read from this file\&.
 .PP
 This file should not be thoughtlessly edited and be copied onto another server\&. If it wants to set the signature intentionally, use the option "\-signature user:xxxx" in afpd\&.conf\&. In this case, afp_signature\&.conf is not used\&.
 .sp .5v
diff --git a/man/man5/afp_voluuid.conf.5.tmpl b/man/man5/afp_voluuid.conf.5.tmpl
new file mode 100644 (file)
index 0000000..c36a112
--- /dev/null
@@ -0,0 +1,77 @@
+'\" t
+.\"     Title: afp_voluuid.conf
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
+.\"      Date: 8 March 2011
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
+.\"  Language: English
+.\"
+.TH "AFP_VOLUUID\&.CONF" "5" "8 March 2011" "Netatalk 2.2" "Netatalk 2.2"
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+afp_voluuid.conf \- Configuration file used by afpd(8) to specify UUID for Time Machine volume
+.SH "DESCRIPTION"
+.PP
+:ETCDIR:/afp_voluuid\&.conf
+is the configuration file used by
+\fBafpd\fR
+to specify UUID of Time Machine volume automagically\&. The configuration lines are composed like:
+.PP
+\fI"volume name"\fR
+\fIuuid\-string\fR
+.PP
+The first field is volume name\&. Volume names must be quoted if they contain spaces\&. The second field is the 36 character hexadecimal ASCII string representation of a UUID\&.
+.PP
+The leading spaces and tabs are ignored\&. Blank lines are ignored\&. The lines prefixed with # are ignored\&. The illegal lines are ignored\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBNote\fR
+.ps -1
+.br
+.PP
+This UUID is advertised by Zeroconf in order to provide robust disambiguation of Time Machine volume\&.
+.PP
+The afpd generates the UUID from random numbers and saves it into afp_voluuid\&.conf, only when setting "tm" option in AppleVolumes file\&.
+.PP
+This file should not be thoughtlessly edited and be copied onto another server\&.
+.sp .5v
+.RE
+.PP
+.SH "EXAMPLES"
+.PP
+\fBExample.\ \&afp_voluuid.conf three TM volumes on one netatalk\fR
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+# This is a comment\&.
+"Backup for John Smith" 1573974F\-0ABD\-69CC\-C40A\-8519B681A0E1
+"bob" 39A487F4\-55AA\-8240\-E584\-69AA01800FE9
+mary 6331E2D1\-446C\-B68C\-3066\-D685AADBE911
+.fi
+.if n \{\
+.RE
+.\}
+.SH "SEE ALSO"
+.PP
+\fBafpd\fR(8),
+\fBAppleVolumes.default\fR(5),
+\fBavahi-daemon\fR(8)
index 0b440d05d7b8fb8aac28241c051713bd1478b31d..b3f099ff1641d7680581c5cecb5c7a7aa5045b2c 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: afpd.conf
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 23 December 2009
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"      Date: 30 Mar 2011
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "AFPD\&.CONF" "5" "23 December 2009" "Netatalk 2.1" "Netatalk 2.1"
+.TH "AFPD\&.CONF" "5" "30 Mar 2011" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -46,7 +46,7 @@ is used instead of a server name, the default server is specified\&. Server name
 .PP
 Each server has to be configured on a
 \fBsingle\fR
-line\&. Though newline escaping is supported\&.
+line\&. Though, using "\e" character, newline escaping is supported\&.
 .sp .5v
 .RE
 The possible options and their meanings are:
@@ -90,7 +90,7 @@ allows guest logins
 .PP
 uams_clrtxt\&.so
 .RS 4
-(uams_pam\&.so or uams_passwd\&.so) Allow logins with passwords transmitted in the clear\&.
+(uams_pam\&.so or uams_passwd\&.so) Allow logins with passwords transmitted in the clear\&. (legacy)
 .RE
 .PP
 uams_randum\&.so
@@ -98,7 +98,7 @@ uams_randum\&.so
 allows Random Number and Two\-Way Random Number Exchange for authentication (requires a separate file containing the passwords, either :ETCDIR:/afppasswd file or the one specified via
 \fB\-passwdfile\fR\&. See
 \fBafppasswd\fR(1)
-for details
+for details\&. (legacy)
 .RE
 .PP
 uams_dhx\&.so
@@ -256,24 +256,24 @@ listens to\&. The default is advertise the first IP address of the system, but t
 when used together with the
 \fB\-proxy\fR
 option\&.
-.PP \fBExample.\ \&IP/hostname example configuration\fR fluxxus \fB\-hostname\fR afp\&.apple\&.com \fB\-ipaddr\fR 127\&.0\&.0\&.1 \fB\-fqdn\fR www\&.microsoft\&.comResult:.TS
-allbox tab(:);
-l
-l
-l.
-T{
-(UTF8) Server name: fluxxus
-T}
-T{
-Listening and advertised network address:
-                    127\&.0\&.0\&.1
-T}
-T{
-Advertised network address:
-                    www\&.microsoft\&.com
-T}
-.TE
-.sp 1
+.PP
+\fBExample.\ \&afpd.conf onfiguration line\fR
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+              fluxxus \-hostname afp\&.example\&.org \-ipaddr 192\&.168\&.0\&.1 \-fqdn www\&.example\&.com
+            
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+
+\fBResult\fR
+.sp
+(UTF8) Server name: fluxxus, Listening and advertised network address: 192\&.168\&.0\&.1, Advertised network address: www\&.example\&.com, hostname is not used\&.
 .RE
 .PP
 \-port \fI[port number]\fR
@@ -293,6 +293,28 @@ server as well, set
 This specifies the DSI server quantum\&. The default value is 303840\&. The maximum value is 0xFFFFFFFFF, the minimum is 32000\&. If you specify a value that is out of range, the default value will be set\&. Do not change this value unless you\'re absolutely sure, what you\'re doing
 .RE
 .PP
+\-dsireadbuf \fI[number]\fR
+.RS 4
+Scale factor that determines the size of the DSI/TCP readahead buffer, default is 12\&. This is multiplies with the DSI server quantum (default ~300k) to give the size of the buffer\&. Increasing this value might increase throughput in fast local networks for volume to volume copies\&.
+\fINote\fR: This buffer is allocated per afpd child process, so specifying large values will eat up large amount of memory (buffer size * number of clients)\&.
+.RE
+.PP
+\-tcprcvbuf \fI[number]\fR
+.RS 4
+Try to set TCP receive buffer using setsockpt()\&. Often OSes impose restrictions on the applications ability to set this value\&.
+.RE
+.PP
+\-tcpsndbuf \fI[number]\fR
+.RS 4
+Try to set TCP send buffer using setsockpt()\&. Often OSes impose restrictions on the applications ability to set this value\&.
+.RE
+.PP
+\-nozeroconf
+.RS 4
+Disable automatic Zeroconf
+service registration if support was compiled in\&.
+.RE
+.PP
 \-slp
 .RS 4
 Register this server using the Service Location Protocol (if SLP
@@ -354,7 +376,14 @@ Immediately unmount volumes removed from AppleVolumes files on SIGHUP sent to th
 .PP
 \-cnidserver \fI[ipaddress:port]\fR
 .RS 4
-Specifies the IP address and port of a cnid_metad server, required for CNID dbd backend\&. Defaults to localhost:4700\&. The network address may be specified either in dotted\-decimal format for IPv4 or in hexadecimal format for IPv6\&.
+Specifies the IP address and port of a cnid_metad server, required for CNID dbd backend\&. Defaults to localhost:4700\&. The network address may be specified either in dotted\-decimal format for IPv4 or in hexadecimal format for IPv6\&.\-
+.RE
+.PP
+\-dircachesize\fI entries\fR
+.RS 4
+Maximum possible entries in the directory cache\&. The cache stores directories and files\&. It is used to cache the full path to directories and CNIDs which considerably speeds up directory enumeration\&.
+.sp
+Default size is 8192, maximum size is 131072\&. Given value is rounded up to nearest power of 2\&. Each entry takes about 100 bytes, which is not much, but remember that every afpd child process for every connected user has its cache\&.
 .RE
 .PP
 \-guestname \fI[name]\fR
@@ -364,7 +393,7 @@ Specifies the user that guests should use (default is "nobody")\&. The name shou
 .PP
 \-[no]icon
 .RS 4
-[Dont\'t] Use the platform\-specific icon\&. Recent Mac OS don\'t display it any longer\&.
+[Don\'t] Use the platform\-specific icon\&. Recent Mac OS don\'t display it any longer\&.
 .RE
 .PP
 \-loginmesg \fI[message]\fR
@@ -420,7 +449,6 @@ Max length of UTF8\-MAC volume name for Mac OS X\&. Note that Hangul is especial
 .nf
 73:  limit of Mac OS X 10\&.1
 80:  limit for Mac OS X 10\&.4/10\&.5 (default)
-123: limit for Mac OS X 10\&.6
 255: limit of spec
 .fi
 .if n \{\
@@ -475,7 +503,7 @@ The config is case\-ignoring
 .RS 4
 .\}
 .nf
-\-setuplog "default log_info /var/log/afpd\&.log"
+\- \-setuplog "default log_info /var/log/afpd\&.log"
 .fi
 .if n \{\
 .RE
@@ -487,7 +515,7 @@ The config is case\-ignoring
 .RS 4
 .\}
 .nf
-\-setuplog "default log_maxdebug /var/log/afpd\&.log"
+\- \-setuplog "default log_maxdebug /var/log/afpd\&.log"
 .fi
 .if n \{\
 .RE
@@ -499,7 +527,7 @@ The config is case\-ignoring
 .RS 4
 .\}
 .nf
-\-setuplog "default log_info /var/log/afpd\&.log"
+\- \-setuplog "default log_info /var/log/afpd\&.log" \e
 \-setuplog "UAMSDaemon log_maxdebug /var/log/uams\&.log"
 .fi
 .if n \{\
@@ -519,7 +547,7 @@ specifying any string as filename is sufficient for the config parser to disting
 .RS 4
 .\}
 .nf
-\-unsetuplog "default \-"
+\- \-unsetuplog "default \-"
 .fi
 .if n \{\
 .RE
@@ -546,7 +574,7 @@ Specify the number of tickles to send before timing out a connection\&. The defa
 .RS 4
 .\}
 .nf
-\- \-transall \-uamlist uams_dhx\&.so,uams_dhx2\&.so
+\- \-tcp \-noddp \-uamlist uams_dhx\&.so,uams_dhx2\&.so \-nosavepassword
 .fi
 .if n \{\
 .RE
@@ -558,7 +586,7 @@ Specify the number of tickles to send before timing out a connection\&. The defa
 .RS 4
 .\}
 .nf
-\- \-transall \-maccodepage mac_cyrillic \-unixcodepage utf8
+\- \-maccodepage mac_cyrillic \-unixcodepage utf8
 .fi
 .if n \{\
 .RE
@@ -570,7 +598,7 @@ Specify the number of tickles to send before timing out a connection\&. The defa
 .RS 4
 .\}
 .nf
-\- \-transall \-uamlist uams_dhx\&.so,uams_dhx2\&.so,uams_guest\&.so,uams_gss\&.so \e 
+\- \-uamlist uams_dhx\&.so,uams_dhx2\&.so,uams_guest\&.so,uams_gss\&.so \e 
 \-k5service afpserver \-k5keytab /path/to/afpserver\&.keytab \e 
 \-k5realm YOUR\&.REALM \-fqdn your\&.fqdn\&.namel:548
 .fi
@@ -586,7 +614,7 @@ Specify the number of tickles to send before timing out a connection\&. The defa
 .nf
 "Guest Server" \-uamlist uams_guest\&.so \-loginmesg "Welcome guest!"
 "User Server" \-uamlist uams_dhx2\&.so \-port 12000
-"special" \-notcp \-defaultvol <path> \-systemvol <path>
+"special" \-ddp \-notcp \-defaultvol <path> \-systemvol <path>
 .fi
 .if n \{\
 .RE
@@ -596,4 +624,5 @@ Specify the number of tickles to send before timing out a connection\&. The defa
 \fBafpd\fR(8),
 \fBafppasswd\fR(1),
 \fBAppleVolumes.default\fR(5),
-\fBafp_signature.conf\fR(5)
+\fBafp_signature.conf\fR(5),
+\fBcnid_metad\fR(8)
index b618c764ef7ba8862a4d00e3b975148fc7f79b35..ed51c8cf3570ef70e23345019c1384636198f392 100644 (file)
@@ -3,11 +3,11 @@
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: 22 September 2000
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "ATALKD\&.CONF" "5" "22 September 2000" "Netatalk 2.1" "Netatalk 2.1"
+.TH "ATALKD\&.CONF" "5" "22 September 2000" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
index 138fb62562937ae07a2db64b5410eef8e14a66e7..6c9b4b748f05dda4fccd91fc92ebfc9863e9194f 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: netatalk.conf
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 9 Jun 2009
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"      Date: 30 Mar 2011
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "NETATALK\&.CONF" "5" "9 Jun 2009" "Netatalk 2.1" "Netatalk 2.1"
+.TH "NETATALK\&.CONF" "5" "30 Mar 2011" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
 .\" * MAIN CONTENT STARTS HERE *
 .\" -----------------------------------------------------------------
 .SH "NAME"
-netatalk.conf \- Configuration file used by netatalk(8) to determine its general configuration
+netatalk.conf \- Configuration file used by Netatalk to determine its general configuration
+.SH "SYNOPSIS"
+.HP \w'\fB:ETCDIR:/netatalk\&.conf\fR\fB\fR\fB:DESTDIR:/etc/default/netatalk\fR\fB\fR\ 'u
+\fB:ETCDIR:/netatalk\&.conf\fR\fB\fR
+.br
+\fB:DESTDIR:/etc/default/netatalk\fR\fB\fR
 .SH "DESCRIPTION"
 .PP
 \fB:ETCDIR:/netatalk\&.conf\fR
 is the configuration file used by afpd to determine what portions of the file system will be shared via AFP, as well as their behaviors\&.
 .PP
+If netatalk has been configured with \-\-enable\-debian, it is not
+\fB:ETCDIR:/netatalk\&.conf\fR
+but
+\fB:DESTDIR:/etc/default/netatalk\fR\&.
+.PP
 Any line not prefixed with
 \fB#\fR
 is interpreted\&. The configuration lines are composed like:
@@ -105,5 +115,8 @@ Set the Unix codepage, used by atalkd and papd to convert extended characters fr
 .RE
 .SH "SEE ALSO"
 .PP
+\fBafpd\fR(8),
+\fBafpd.conf\fR(5),
+\fBcnid_metad\fR(8),
 \fBatalkd\fR(8),
 \fBatalkd.conf\fR(5)
index ca8103fa144dfafbc29868a473e8812a4212ebfb..caf5f60e958210ca365ce4982609dfd76c86a0d8 100644 (file)
@@ -3,11 +3,11 @@
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: 06 Sep 2004
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "PAPD\&.CONF" "5" "06 Sep 2004" "Netatalk 2.1" "Netatalk 2.1"
+.TH "PAPD\&.CONF" "5" "06 Sep 2004" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
index 99d41e391b953aa99736dd2a5640640c4da69031..26273d4378663bcbdaabe2e167fffd4a085cd202 100644 (file)
@@ -14,12 +14,17 @@ SUFFIXES = .tmpl .
            <$< >$@
 
 NONGENERATED_MANS = timelord.8
-GENERATED_MANS    = afp_acls.8 afpd.8 atalkd.8 cnid_dbd.8 cnid_metad.8 papd.8 papstatus.8 psf.8
-TEMPLATE_FILES    = afp_acls.8.tmpl afpd.8.tmpl atalkd.8.tmpl cnid_dbd.8.tmpl \
-       cnid_metad.8.tmpl papd.8.tmpl papstatus.8.tmpl psf.8.tmpl
+GENERATED_MANS    = afpd.8 cnid_dbd.8 cnid_metad.8
+TEMPLATE_FILES    = afpd.8.tmpl cnid_dbd.8.tmpl cnid_metad.8.tmpl
+ATALK_MANS        = atalkd.8.tmpl papd.8.tmpl papstatus.8.tmpl psf.8.tmpl
+
+if USE_APPLETALK
+GENERATED_MANS += atalkd.8 papd.8 papstatus.8 psf.8
+TEMPLATE_FILES += $(ATALK_MANS)
+endif
 
 man_MANS = $(GENERATED_MANS) $(NONGENERATED_MANS)
 
 CLEANFILES = $(GENERATED_MANS)
 
-EXTRA_DIST = $(TEMPLATE_FILES) $(NONGENERATED_MANS)
+EXTRA_DIST = $(TEMPLATE_FILES) $(NONGENERATED_MANS) $(ATALK_MANS)
diff --git a/man/man8/afp_acls.8.tmpl b/man/man8/afp_acls.8.tmpl
deleted file mode 100644 (file)
index c09f03b..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-'\" t
-.\"     Title: afp_acls
-.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 02 Feb 2009
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
-.\"  Language: English
-.\"
-.TH "AFP_ACLS" "8" "02 Feb 2009" "Netatalk 2.1" "Netatalk 2.1"
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-afp_acls \- Setup and Usage Howto for ACLs with Netatalk
-.SH "DESCRIPTION"
-.PP
-ACL support for AFP is implemented with NFSv4 ACLs\&. Few filesystems and fewer OSes support these\&. At the time of implementation its only provided with ZFS on Solaris, Opensolaris and derived distributions\&.
-.SH "CONFIGURATION"
-.PP
-In order to be able to support ACLs, the following things have to be configured:
-.sp
-.RS 4
-.ie n \{\
-\h'-04' 1.\h'+01'\c
-.\}
-.el \{\
-.sp -1
-.IP "  1." 4.2
-.\}
-ZFS Volumes
-.sp
-You MUST configure two ACL parameters for any volume you want to use with Netatalk:
-.sp
-.if n \{\
-.RS 4
-.\}
-.nf
-aclinherit = passthrough
-aclmode = passthrough
-.fi
-.if n \{\
-.RE
-.\}
-.sp
-For an explanation of what these parameters mean and how to apply them see, your hosts ZFS documentation (e\&.g\&. man zfs)\&.
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04' 2.\h'+01'\c
-.\}
-.el \{\
-.sp -1
-.IP "  2." 4.2
-.\}
-Authentication Domain
-.sp
-Your server and the clients must be part of a security association where identity data is coming from a common source\&. ACLs in Darwin are based on UUIDs and so is the ACL specification in AFP 3\&.2\&. Therefor your source of identity data has to provide an attribute for every user and group where a UUID is stored as a ASCII string\&.
-.sp
-In other words:
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-you need an Open Directory Server or an LDAP server where you store UUIDs in some attribute
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-your clients must be configured to use this server
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-your server should be configured to use this server via nsswitch and PAM\&.
-.if n \{\
-.sp
-.\}
-.RS 4
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBTip\fR
-.ps -1
-.br
-This however is not a strict requirement: if you create duplicates of every LDAP/OD user and group with identic attributes (name, uid, gid) in your local data store (/etc/[passwd|group]) ACLs will work
-\fIas long as user/group names/ids in the filesystem are equal to their counterparts in the LDAP/OD datastore\fR\&.
-.sp .5v
-.RE
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-configure Netatalk via afp_ldap\&.conf so that Netatalk is able to retrieve the UUID for users and groups via LDAP search queries
-.RE
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04' 3.\h'+01'\c
-.\}
-.el \{\
-.sp -1
-.IP "  3." 4.2
-.\}
-Netatalk Volumes
-.sp
-Finally you can add
-\fBoptions:acls\fR
-to your volume defintion to add ACL support\&. In case your volume basedir doesn\'t grant read permissions via mode (like:
-\fB0700 root:adm\fR) but only via ACLs, you MUST add the
-\fBnostat\fR
-option to the volume defintion\&.
-.RE
-.SH "SEE ALSO"
-.PP
-\fBafp_ldap.conf\fR(5),
-\fBAppleVolumes.default\fR(5)
index 1995473a7ae58170221b20a9ff88ae092eb14111..9fc6dd3739e148722266333b3145fe3c89bc6dc8 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: afpd
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 21 Apr 2010
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"      Date: 08 Mar 2011
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "AFPD" "8" "21 Apr 2010" "Netatalk 2.1" "Netatalk 2.1"
+.TH "AFPD" "8" "08 Mar 2011" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -278,7 +278,17 @@ user\'s list of volumes to mount
 list of server signature
 .RE
 .PP
-:ETCDIR:/netatalk/msg/message\&.pid
+:ETCDIR:/afp_voluuid\&.conf
+.RS 4
+list of UUID for Time Machine volume
+.RE
+.PP
+:ETCDIR:/afp_ldap\&.conf
+.RS 4
+configuration file for LDAP and ACL support
+.RE
+.PP
+:ETCDIR:/msg/message\&.pid
 .RS 4
 contains messages to be sent to users\&.
 .RE
@@ -290,4 +300,6 @@ contains messages to be sent to users\&.
 \fBafpd.conf\fR(5),
 \fBAppleVolumes.default\fR(5),
 \fBafp_signature.conf\fR(5),
+\fBafp_voluuid.conf\fR(5),
+\fBafp_ldap.conf\fR(5),
 \fBdbd\fR(1)\&.
index 3f478fab24a4b049406beb30ed608f3da9b5e7ec..819e3889623e5350c4b5ff91e40ba1a0c976b45a 100644 (file)
@@ -3,11 +3,11 @@
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: 06 Sep 2004
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "ATALKD" "8" "06 Sep 2004" "Netatalk 2.1" "Netatalk 2.1"
+.TH "ATALKD" "8" "06 Sep 2004" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
index 5fc967a55bd7f8f21a37ef63b90ae696ad3223b7..090ab7724413234efa19c5d661236318e0438854 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: cnid_dbd
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: 21 Mar 2009
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"      Date: 10 Dec 2010
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "CNID_DBD" "8" "21 Mar 2009" "Netatalk 2.1" "Netatalk 2.1"
+.TH "CNID_DBD" "8" "10 Dec 2010" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -22,7 +22,7 @@
 cnid_dbd \- implement access to CNID databases through a dedicated daemon process
 .SH "SYNOPSIS"
 .HP \w'\fBcnid_dbd\fR\fB\fR\fB\fR\fB\fR\ 'u
-\fBcnid_dbd\fR\fB\fR\fB\fR\fB\fR \fIdbdir\fR \fIctrlfd\fR \fIclntfd\fR \fIlogconfig_string\fR
+\fBcnid_dbd\fR\fB\fR\fB\fR\fB\fR \fIvolpath\fR \fIctrlfd\fR \fIclntfd\fR \fIlogconfig_string\fR
 .SH "DESCRIPTION"
 .PP
 \fBcnid_dbd\fR
@@ -41,7 +41,7 @@ backend\&.
 \fBcnid_dbd\fR
 is never started via the command line or system startup scripts but only by the
 \fBcnid_metad\fR
-daemon\&. There is at most one instance of
+daemon\&. There is one instance of
 \fBcnid_dbd\fR
 per netatalk volume\&.
 .PP
@@ -53,11 +53,6 @@ database library and uses transactionally protected updates\&. The
 backend with transactions will avoid corruption of the CNID database even if the system crashes unexpectedly\&.
 .PP
 \fBcnid_dbd\fR
-uses the same on\-disk database format as the
-\fBcdb\fR
-backend\&. It is therefore possible to switch between the two backends as necessary\&.
-.PP
-\fBcnid_dbd\fR
 inherits the effective userid and groupid from
 \fBcnid_metad\fR
 on startup, which is normally caused by
@@ -105,7 +100,7 @@ database subsystem will create files named log\&.xxxxxxxxxx in the database home
 \fBlogfile_autoremove\fR
 option is specified in the
 \fIdb_param\fR
-configuration file (see below)\&.
+configuration file (see below) with a value of 0 (default 1)\&.
 .PP
 Do not use
 \fBcnid_dbd\fR
@@ -180,7 +175,11 @@ exits\&. Default: 600\&. Set this to 0 to disable the timeout\&.
 .RE
 .SH "UPDATING"
 .PP
-In order to update between Netatalk releases using different BerkeleyDB library versions, follow this steps:
+Note that the first version to appear
+\fIafter\fR
+Netatalk 2\&.1 ie Netatalk 2\&.1\&.1, will support BerkeleyDB updates on the fly without manual intervention\&. In other words Netatalk 2\&.1 does contain code to prepare the BerkeleyDB database for upgrades and to upgrade it in case it has been prepared before\&. That means it can\'t upgrade a 2\&.0\&.x version because that one didn\'t prepare the database\&.
+.PP
+In order to update between older Netatalk releases using different BerkeleyDB library versions, follow this steps:
 .sp
 .RS 4
 .ie n \{\
@@ -239,10 +238,6 @@ Again using the new BerkeleyDB utilities run
 .\}
 Start the the new version of Netatalk
 .RE
-.PP
-Note that the first version to appear
-\fIafter\fR
-Netatalk 2\&.1 ie Netatalk 2\&.1\&.1, will support BerkeleyDB updates on the fly without manual intervention\&. In other words Netatalk 2\&.1 does contain code to prepare the BerkeleyDB database for upgrades and to upgrade it in case it has been prepared before\&. That means it can\'t upgrade a 2\&.0\&.x version because that one didn\'t prepare the database\&.
 .SH "SEE ALSO"
 .PP
 \fBcnid_metad\fR(8),
index 1698faab2fc51bb0d837230fd78c82f6ab1e83cc..2822e2f098968bb8e3623fe473dbf675af9e686d 100644 (file)
@@ -3,11 +3,11 @@
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: 23 Dec 2009
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "CNID_METAD" "8" "23 Dec 2009" "Netatalk 2.1" "Netatalk 2.1"
+.TH "CNID_METAD" "8" "23 Dec 2009" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
index 3e94ed8bd86211237d9b8f1c98bf0514a9ca2e8d..c48cf80061809b1d88aa206c816b6b627a29916b 100644 (file)
@@ -3,11 +3,11 @@
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: 06 September 2004
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "PAPD" "8" "06 September 2004" "Netatalk 2.1" "Netatalk 2.1"
+.TH "PAPD" "8" "06 September 2004" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
index ad4b6fbde4b9c17f8391140b3f56247a05ae55e0..d0bc11d00cb60bb95b2752e91dfb64709de12120 100644 (file)
@@ -3,11 +3,11 @@
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: 17 Dec 1991
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "PAPSTATUS" "8" "17 Dec 1991" "Netatalk 2.1" "Netatalk 2.1"
+.TH "PAPSTATUS" "8" "17 Dec 1991" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
index 18913946b3dd389a317c82ecb9e01af15671d69f..ed9f59e20e2ca584090c87a25e48344e6221f6c2 100644 (file)
@@ -3,11 +3,11 @@
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: 17 Dec 1991
-.\"    Manual: Netatalk 2.1
-.\"    Source: Netatalk 2.1
+.\"    Manual: Netatalk 2.2
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "PSF" "8" "17 Dec 1991" "Netatalk 2.1" "Netatalk 2.1"
+.TH "PSF" "8" "17 Dec 1991" "Netatalk 2.2" "Netatalk 2.2"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
index 46a6829300b18121f01aa7a1ff5cad5b974a410c..14964862fecadce1c362327d464561158990d253 100644 (file)
@@ -4,10 +4,10 @@
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
 .\"      Date: 27 Jun 2001
 .\"    Manual: The Netatalk Project
-.\"    Source: Netatalk 2.1
+.\"    Source: Netatalk 2.2
 .\"  Language: English
 .\"
-.TH "TIMELORD" "8" "27 Jun 2001" "Netatalk 2.1" "The Netatalk Project"
+.TH "TIMELORD" "8" "27 Jun 2001" "Netatalk 2.2" "The Netatalk Project"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
diff --git a/test/.gitignore b/test/.gitignore
new file mode 100644 (file)
index 0000000..282522d
--- /dev/null
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
diff --git a/test/Makefile.am b/test/Makefile.am
new file mode 100644 (file)
index 0000000..bfd9288
--- /dev/null
@@ -0,0 +1 @@
+SUBDIRS = afpd
diff --git a/test/afpd/.gitignore b/test/afpd/.gitignore
new file mode 100644 (file)
index 0000000..4d6beea
--- /dev/null
@@ -0,0 +1,7 @@
+Makefile
+Makefile.in
+.deps
+.libs
+test
+test.conf
+test.default
\ No newline at end of file
diff --git a/test/afpd/Makefile.am b/test/afpd/Makefile.am
new file mode 100644 (file)
index 0000000..2bb6b3e
--- /dev/null
@@ -0,0 +1,65 @@
+# Makefile.am for test/afpd/
+
+pkgconfdir = @PKGCONFDIR@
+
+TESTS = test.sh test
+
+check_PROGRAMS = test
+noinst_HEADERS = test.h subtests.h afpfunc_helpers.h
+EXTRA_DIST = test.sh
+CLEANFILES = test.default test.conf
+
+test_SOURCES =  test.c subtests.c afpfunc_helpers.c \
+                               $(top_builddir)/etc/afpd/afp_avahi.c \
+                               $(top_builddir)/etc/afpd/afp_config.c \
+                               $(top_builddir)/etc/afpd/afp_dsi.c \
+                               $(top_builddir)/etc/afpd/afp_options.c \
+                               $(top_builddir)/etc/afpd/afp_util.c \
+                               $(top_builddir)/etc/afpd/afprun.c \
+                               $(top_builddir)/etc/afpd/appl.c \
+                               $(top_builddir)/etc/afpd/auth.c \
+                               $(top_builddir)/etc/afpd/afp_zeroconf.c \
+                               $(top_builddir)/etc/afpd/catsearch.c \
+                               $(top_builddir)/etc/afpd/desktop.c \
+                               $(top_builddir)/etc/afpd/dircache.c \
+                               $(top_builddir)/etc/afpd/directory.c \
+                               $(top_builddir)/etc/afpd/enumerate.c \
+                               $(top_builddir)/etc/afpd/extattrs.c \
+                               $(top_builddir)/etc/afpd/file.c \
+                               $(top_builddir)/etc/afpd/filedir.c \
+                               $(top_builddir)/etc/afpd/fork.c \
+                               $(top_builddir)/etc/afpd/gettok.c \
+                               $(top_builddir)/etc/afpd/hash.c \
+                               $(top_builddir)/etc/afpd/mangle.c \
+                               $(top_builddir)/etc/afpd/messages.c \
+                               $(top_builddir)/etc/afpd/nfsquota.c \
+                               $(top_builddir)/etc/afpd/ofork.c \
+                               $(top_builddir)/etc/afpd/quota.c \
+                               $(top_builddir)/etc/afpd/status.c \
+                               $(top_builddir)/etc/afpd/switch.c \
+                               $(top_builddir)/etc/afpd/uam.c \
+                               $(top_builddir)/etc/afpd/unix.c \
+                               $(top_builddir)/etc/afpd/volume.c
+
+if HAVE_ACLS
+test_SOURCES += $(top_builddir)/etc/afpd/acls.c
+endif
+
+test_CFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/etc/afpd \
+        @SLP_CFLAGS@ @ZEROCONF_CFLAGS@ \
+        -DAPPLCNAME \
+        -DSERVERTEXT=\"$(SERVERTEXT)/\" \
+        -D_PATH_AFPDDEFVOL=\"$(pkgconfdir)/AppleVolumes.default\" \
+        -D_PATH_AFPDSYSVOL=\"$(pkgconfdir)/AppleVolumes.system\" \
+        -D_PATH_AFPDPWFILE=\"$(pkgconfdir)/afppasswd\" \
+        -D_PATH_AFPDCONF=\"$(pkgconfdir)/afpd.conf\" \
+        -D_PATH_AFPDUAMPATH=\"$(UAMS_PATH)/\" \
+        -D_PATH_ACL_LDAPCONF=\"$(pkgconfdir)/afp_ldap.conf\" \
+        -D_PATH_AFPDSIGCONF=\"$(pkgconfdir)/afp_signature.conf\" \
+        -D_PATH_AFPDUUIDCONF=\"$(pkgconfdir)/afp_voluuid.conf\"
+
+test_LDADD = $(top_builddir)/libatalk/cnid/libcnid.la \
+       $(top_builddir)/libatalk/libatalk.la \
+       @QUOTA_LIBS@ @SLP_LIBS@ @WRAP_LIBS@ @LIBADD_DL@ @ACL_LIBS@ @ZEROCONF_LIBS@ @PTHREAD_LIBS@
+
+test_LDFLAGS = -export-dynamic
diff --git a/test/afpd/afpfunc_helpers.c b/test/afpd/afpfunc_helpers.c
new file mode 100644 (file)
index 0000000..b5b8165
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+  $Id: afpfunc_helpers.c,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
+  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+  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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+#include <atalk/queue.h>
+#include <atalk/bstrlib.h>
+
+#include "file.h"
+#include "filedir.h"
+#include "directory.h"
+#include "dircache.h"
+#include "hash.h"
+#include "globals.h"
+#include "afp_config.h"
+#include "volume.h"
+
+#include "test.h"
+#include "subtests.h"
+
+
+#define rbufsize 128000
+static char rbuf[rbufsize];
+static size_t rbuflen;
+
+#define ADD(a, b, c) (a) += (c); \
+                         (b) += (c)
+
+#define PUSHBUF(p, val, size, len) \
+    memcpy((p), (val), (size));    \
+    (p) += (size);                 \
+    (len) += (size)
+
+#define PUSHVAL(p, type, val, len)         \
+    { \
+        type type = val;                          \
+        memcpy(p, &type, sizeof(type));           \
+        (p) += sizeof(type);                      \
+        (len) += sizeof(type);                    \
+    }
+
+static int push_path(char **bufp, const char *name)
+{
+    int len = 0;
+    int slen = strlen(name);
+    char *p = *bufp;
+
+    PUSHVAL(p, uint8_t, 3, len); /* path type */
+    PUSHVAL(p, uint32_t, kTextEncodingUTF8, len); /* text encoding hint */
+    PUSHVAL(p, uint16_t, htons(slen), len);
+    if (slen) {
+        for (int i = 0; i < slen; i++) {
+            if (name[i] == '/')
+                p[i] = 0;
+            else
+                p[i] = name[i];
+        }
+        len += slen;
+    }
+
+    *bufp += len;
+    return len;
+}
+
+/***********************************************************************************
+ * Interface
+ ***********************************************************************************/
+
+char **cnamewrap(const char *name)
+{
+    static char buf[256];
+    static char *p = buf;
+    int len = 0;
+
+    PUSHVAL(p, uint8_t, 3, len); /* path type */
+    PUSHVAL(p, uint32_t, kTextEncodingUTF8, len); /* text encoding hint */
+    PUSHVAL(p, uint16_t, ntohs(strlen(name)), len);
+    strcpy(p, name);
+
+    p = buf;
+    return &p;
+}
+
+int getfiledirparms(AFPObj *obj, uint16_t vid, cnid_t did, const char *name)
+{
+    const int bufsize = 256;
+    char buf[bufsize];
+    char *p = buf;
+    int len = 0;
+
+    ADD(p, len , 2);
+
+    PUSHVAL(p, uint16_t, vid, len);
+    PUSHVAL(p, cnid_t, did, len);
+    PUSHVAL(p, uint16_t, htons(FILPBIT_FNUM | FILPBIT_PDINFO), len);
+    PUSHVAL(p, uint16_t, htons(DIRPBIT_DID | DIRPBIT_PDINFO), len);
+
+    len += push_path(&p, name);
+
+    return afp_getfildirparams(obj, buf, len, rbuf, &rbuflen);
+}
+
+int createdir(AFPObj *obj, uint16_t vid, cnid_t did, const char *name)
+{
+    const int bufsize = 256;
+    char buf[bufsize];
+    char *p = buf;
+    int len = 0;
+
+    ADD(p, len , 2);
+
+    PUSHVAL(p, uint16_t, vid, len);
+    PUSHVAL(p, cnid_t, did, len);
+    len += push_path(&p, name);
+
+    return afp_createdir(obj, buf, len, rbuf, &rbuflen);
+}
+
+int createfile(AFPObj *obj, uint16_t vid, cnid_t did, const char *name)
+{
+    const int bufsize = 256;
+    char buf[bufsize];
+    char *p = buf;
+    int len = 0;
+
+    PUSHVAL(p, uint16_t, htons(128), len); /* hard create */
+    PUSHVAL(p, uint16_t, vid, len);
+    PUSHVAL(p, cnid_t, did, len);
+    len += push_path(&p, name);
+
+    return afp_createfile(obj, buf, len, rbuf, &rbuflen);
+}
+
+int delete(AFPObj *obj, uint16_t vid, cnid_t did, const char *name)
+{
+    const int bufsize = 256;
+    char buf[bufsize];
+    char *p = buf;
+    int len = 0;
+
+    PUSHVAL(p, uint16_t, htons(128), len); /* hard create */
+    PUSHVAL(p, uint16_t, vid, len);
+    PUSHVAL(p, cnid_t, did, len);
+    len += push_path(&p, name);
+
+    return afp_delete(obj, buf, len, rbuf, &rbuflen);
+}
+
+int enumerate(AFPObj *obj, uint16_t vid, cnid_t did)
+{
+    const int bufsize = 256;
+    char buf[bufsize];
+    char *p = buf;
+    int len = 0;
+
+    ADD(p, len , 2);
+
+    PUSHVAL(p, uint16_t, vid, len);
+    PUSHVAL(p, cnid_t, did, len);
+    PUSHVAL(p, uint16_t, htons(FILPBIT_PDID | FILPBIT_FNUM | FILPBIT_PDINFO), len);
+    PUSHVAL(p, uint16_t, htons(DIRPBIT_PDID | DIRPBIT_DID | DIRPBIT_PDINFO), len);
+    PUSHVAL(p, uint16_t, htons(20), len);       /* reqcount */
+    PUSHVAL(p, uint32_t, htonl(1), len);        /* startindex */
+    PUSHVAL(p, uint32_t, htonl(rbufsize), len); /* max replysize */
+
+    len += push_path(&p, "");
+
+    return afp_enumerate_ext2(obj, buf, len, rbuf, &rbuflen);
+}
+
+uint16_t openvol(AFPObj *obj, const char *name)
+{
+    int ret;
+    uint16_t bitmap;
+    uint16_t vid;
+    const int bufsize = 32;
+    char buf[bufsize];
+    char *p = buf;
+    char len = strlen(name);
+
+    memset(p, 0, bufsize);
+    p += 2;
+
+    /* bitmap */
+    bitmap = htons(1<<VOLPBIT_VID);
+    memcpy(p, &bitmap, 2);
+    p += 2;
+
+    /* name */
+    *p = len;
+    p++;
+    memcpy(p, name, len);
+    p += len;
+
+    len += 2 + 2 + 1; /* (command+pad) + bitmap + len */
+    if (len & 1)
+        len++;
+
+    rbuflen = 0;
+    if ((ret = afp_openvol(obj, buf, len, rbuf, &rbuflen)) != AFP_OK)
+        return 0;
+
+    p = rbuf;
+    memcpy(&bitmap, p, 2);
+    p += 2;
+    bitmap = ntohs(bitmap);
+    if ( ! (bitmap & 1<<VOLPBIT_VID))
+        return 0;
+
+    memcpy(&vid, p, 2);
+    return vid;
+}
+
diff --git a/test/afpd/afpfunc_helpers.h b/test/afpd/afpfunc_helpers.h
new file mode 100644 (file)
index 0000000..3fda7df
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+  $Id: afpfunc_helpers.h,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
+  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+  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.
+*/
+
+#ifndef AFPFUNC_HELPERS
+#define AFPFUNC_HELPERS
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+#include <atalk/queue.h>
+#include <atalk/bstrlib.h>
+
+#include "file.h"
+#include "filedir.h"
+#include "directory.h"
+#include "dircache.h"
+#include "hash.h"
+#include "globals.h"
+#include "afp_config.h"
+#include "volume.h"
+
+#include "test.h"
+#include "subtests.h"
+
+extern char **cnamewrap(const char *name);
+
+extern int getfiledirparms(AFPObj *obj, uint16_t vid, cnid_t did, const char *name);
+extern int createdir(AFPObj *obj, uint16_t vid, cnid_t did, const char *name);
+extern int createfile(AFPObj *obj, uint16_t vid, cnid_t did, const char *name);
+extern int delete(AFPObj *obj, uint16_t vid, cnid_t did, const char *name);
+extern int enumerate(AFPObj *obj, uint16_t vid, cnid_t did);
+extern uint16_t openvol(AFPObj *obj, const char *name);
+
+#endif  /* AFPFUNC_HELPERS */
diff --git a/test/afpd/subtests.c b/test/afpd/subtests.c
new file mode 100644 (file)
index 0000000..6e76a18
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+  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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+#include <atalk/queue.h>
+#include <atalk/bstrlib.h>
+
+#include "directory.h"
+#include "dircache.h"
+#include "hash.h"
+#include "globals.h"
+#include "afp_config.h"
+#include "volume.h"
+
+#include "test.h"
+#include "subtests.h"
+
+static int reti;                /* for the TEST_int macro */
+
+int test001_add_x_dirs(const struct vol *vol, cnid_t start, cnid_t end)
+{
+    struct dir *dir;
+    char dirname[20];
+    while (start++ < end) {
+        sprintf(dirname, "dir%04u", start);
+        dir = dir_new(dirname, dirname, vol, DIRDID_ROOT, htonl(start), bfromcstr(vol->v_path), 0);
+        if (dir == NULL)
+            return -1;
+        if (dircache_add(vol, dir) != 0)
+            return -1;
+    }
+
+    return 0;
+}
+
+int test002_rem_x_dirs(const struct vol *vol, cnid_t start, cnid_t end)
+{
+    struct dir *dir;
+    while (start++ < end) {
+        if ((dir = dircache_search_by_did(vol, htonl(start))))
+            if (dir_remove(vol, dir) != 0)
+                return -1;
+    }
+
+    return 0;
+}
diff --git a/test/afpd/subtests.h b/test/afpd/subtests.h
new file mode 100644 (file)
index 0000000..1ce10a0
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+  $Id: subtests.h,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
+  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+  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.
+*/
+
+#ifndef SUBTESTS_H
+#define SUBTESTS_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+#include <atalk/queue.h>
+#include <atalk/bstrlib.h>
+
+#include "directory.h"
+#include "dircache.h"
+#include "hash.h"
+#include "globals.h"
+#include "afp_config.h"
+#include "volume.h"
+
+extern int test001_add_x_dirs(const struct vol *vol, cnid_t start, cnid_t end);
+extern int test002_rem_x_dirs(const struct vol *vol, cnid_t start, cnid_t end);
+#endif  /* SUBTESTS_H */
diff --git a/test/afpd/test.c b/test/afpd/test.c
new file mode 100644 (file)
index 0000000..c554260
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+  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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+#include <atalk/queue.h>
+#include <atalk/bstrlib.h>
+
+#include "file.h"
+#include "filedir.h"
+#include "directory.h"
+#include "dircache.h"
+#include "hash.h"
+#include "globals.h"
+#include "afp_config.h"
+#include "volume.h"
+
+#include "test.h"
+#include "subtests.h"
+#include "afpfunc_helpers.h"
+
+/* Stuff from main.c which of cource can't be added as source to testbin */
+unsigned char nologin = 0;
+struct afp_options default_options;
+static AFPConfig *configs;
+
+/* Static variables */
+
+int main(int argc, char **argv)
+{
+    #define ARGNUM 7
+    char *args[ARGNUM] = {"test", "-F", "test.conf", "-f", "test.default", "-s" ,"test.system"};
+    int reti;
+    uint16_t vid;
+    struct vol *vol;
+    struct dir *retdir;
+    struct path *path;
+
+    /* initialize */
+    afp_version = 32;
+    printf("Initializing\n============\n");
+    TEST(setuplog("default log_note /dev/tty"));
+    TEST(afp_options_init(&default_options));
+    TEST_int(afp_options_parse( ARGNUM, args, &default_options), 1);
+    TEST_expr(configs = configinit(&default_options), configs != NULL);
+    TEST(cnid_init());
+    TEST(load_volumes(&configs->obj));
+    TEST_int(dircache_init(8192), 0);
+    printf("\n");
+
+    /* now run tests */
+    printf("Running tests\n=============\n");
+
+    TEST_expr(vid = openvol(&configs->obj, "test"), vid != 0);
+    TEST_expr(vol = getvolbyvid(vid), vol != NULL);
+
+    /* test directory.c stuff */
+    TEST_expr(retdir = dirlookup(vol, DIRDID_ROOT_PARENT), retdir != NULL);
+    TEST_expr(retdir = dirlookup(vol, DIRDID_ROOT), retdir != NULL);
+    TEST_expr(path = cname(vol, retdir, cnamewrap("Network Trash Folder")), path != NULL);
+
+    TEST_expr(retdir = dirlookup(vol, DIRDID_ROOT), retdir != NULL);
+    TEST_int(getfiledirparms(&configs->obj, vid, DIRDID_ROOT_PARENT, "test"), 0);
+    TEST_int(getfiledirparms(&configs->obj, vid, DIRDID_ROOT, ""), 0);
+
+    TEST_expr(reti = createdir(&configs->obj, vid, DIRDID_ROOT, "dir1"),
+              reti == 0 || reti == AFPERR_EXIST);
+
+    TEST_int(getfiledirparms(&configs->obj, vid, DIRDID_ROOT, "dir1"), 0);
+/*
+  FIXME: this doesn't work although it should. "//" get translated to \000 \000 at means ".."
+  ie this should getfiledirparms for DIRDID_ROOT_PARENT -- at least afair!
+    TEST_int(getfiledirparms(&configs->obj, vid, DIRDID_ROOT, "//"), 0);
+*/
+    TEST_int(createfile(&configs->obj, vid, DIRDID_ROOT, "dir1/file1"), 0);
+    TEST_int(delete(&configs->obj, vid, DIRDID_ROOT, "dir1/file1"), 0);
+    TEST_int(delete(&configs->obj, vid, DIRDID_ROOT, "dir1"), 0);
+
+    TEST_int(createfile(&configs->obj, vid, DIRDID_ROOT, "file1"), 0);
+    TEST_int(getfiledirparms(&configs->obj, vid, DIRDID_ROOT, "file1"), 0);
+    TEST_int(delete(&configs->obj, vid, DIRDID_ROOT, "file1"), 0);
+
+
+    /* test enumerate.c stuff */
+    TEST_int(enumerate(&configs->obj, vid, DIRDID_ROOT), 0);
+}
diff --git a/test/afpd/test.h b/test/afpd/test.h
new file mode 100644 (file)
index 0000000..802120a
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+  $Id: test.h,v 1.1.2.1 2010-02-01 10:56:08 franklahm Exp $
+  Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
+
+  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.
+*/
+
+#ifndef TEST_H
+#define TEST_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <atalk/util.h>
+#include <atalk/cnid.h>
+#include <atalk/logger.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+#include <atalk/queue.h>
+#include <atalk/bstrlib.h>
+
+#include "directory.h"
+#include "dircache.h"
+#include "hash.h"
+#include "globals.h"
+#include "afp_config.h"
+#include "volume.h"
+#include "subtests.h"
+
+static inline void alignok(int len)
+{
+    int i = 1;
+    if (len < 80)
+        i = 80 - len;
+    while (i--)
+        printf(" ");
+}
+
+#define TEST(a) \
+    printf("Testing: %s ... ", (#a) ); \
+    alignok(strlen(#a));               \
+    a;                                 \
+    printf("[ok]\n");
+
+#define TEST_int(a, b) \
+    printf("Testing: %s ... ", (#a) );            \
+    alignok(strlen(#a));                          \
+    if ((reti = (a)) != b) {                      \
+        printf("[error]\n");                      \
+        exit(1);                                  \
+    } else { printf("[ok]\n"); }
+
+#define TEST_expr(a, b)                              \
+    printf("Testing: %s ... ", (#a) );               \
+    alignok(strlen(#a));                             \
+    a;                                               \
+    if (b) {                                         \
+        printf("[ok]\n");                            \
+    } else {                                         \
+        printf("[error]\n");                         \
+        exit(1);                                     \
+    }
+#endif  /* TEST_H */
diff --git a/test/afpd/test.sh b/test/afpd/test.sh
new file mode 100755 (executable)
index 0000000..80066b6
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+if [ ! -d /tmp/AFPtestvolume ] ; then
+    mkdir -p /tmp/AFPtestvolume
+    if [ $? -ne 0 ] ; then
+        echo Error creating AFP test volume /tmp/AFPtestvolume
+        exit 1
+    fi
+fi
+
+if [ ! -f test.conf ] ; then
+    echo -n "Creating configuration template ... "
+    cat > test.conf <<EOF
+test -noddp -port 10548
+EOF
+    echo [ok]
+fi
+
+if [ ! -f test.default ] ; then
+    echo -n "Creating volume config template ... "
+    cat > test.default <<EOF
+/tmp/AFPtestvolume "test" ea:none
+EOF
+    echo [ok]
+fi