5821 Sparc AI microroot is not fully writable
5802 system calls to find should be investigated to see if an os.walk would be better
--- a/usr/src/cmd/distro_const/DC-manifest.rng Thu Jan 08 13:23:22 2009 -0700
+++ b/usr/src/cmd/distro_const/DC-manifest.rng Thu Jan 08 17:06:12 2009 -0800
@@ -18,7 +18,7 @@
CDDL HEADER END
-Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+Copyright 2009 Sun Microsystems, Inc. All rights reserved.
Use is subject to license terms.
-->
@@ -338,14 +338,11 @@
<element name="bootroot_contents">
<interleave>
<oneOrMore>
- <element name="base_include">
- <ref name="nm_filetype_attr"/>
- <text/>
- </element>
+ <ref name="nm_base_include"/>
</oneOrMore>
<zeroOrMore>
<element name="base_exclude">
- <ref name="nm_filetype_attr"/>
+ <ref name="nm_excltype_attr"/>
<text/>
</element>
</zeroOrMore>
@@ -356,15 +353,61 @@
</element>
</define>
- <define name="nm_filetype_attr">
- <optional>
+ <!--
+ Files to be included may be specified as individual files or as
+ directories of files. On some platforms per-file compression is done by
+ default. This can be disabled on individual files which are read in as
+ part of a directory of files, by base_include'ing that file and
+ specifying fiocompress="false" for it.
+ -->
+
+ <define name="nm_base_include">
+ <element name="base_include">
+ <choice>
+ <!-- Can be either files or dirs,
+ but files allow more attributes. -->
+ <ref name="nm_inclfiletype_attr"/>
+ <ref name="nm_incldirtype_attr"/>
+ </choice>
+ <text/>
+ </element>
+ </define>
+
+ <define name="nm_inclfiletype_attr">
+ <group>
+ <!-- In the case of "type = file", allow fiocompress -->
+ <attribute name="type">
+ <choice>
+ <value>file</value>
+ </choice>
+ </attribute>
+ <optional>
+ <attribute name="fiocompress">
+ <data type="boolean"/>
+ </attribute>
+ </optional>
+ </group>
+ </define>
+
+ <define name="nm_incldirtype_attr">
+ <group>
+ <attribute name="type">
+ <choice>
+ <value>dir</value>
+ </choice>
+ </attribute>
+ </group>
+ </define>
+
+ <define name="nm_excltype_attr">
+ <group>
<attribute name="type">
<choice>
<value>file</value>
<value>dir</value>
</choice>
</attribute>
- </optional>
+ </group>
</define>
<!--
--- a/usr/src/cmd/distro_const/DC_defs.py Thu Jan 08 13:23:22 2009 -0700
+++ b/usr/src/cmd/distro_const/DC_defs.py Thu Jan 08 17:06:12 2009 -0800
@@ -18,7 +18,7 @@
#
# CDDL HEADER END
#
-# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
@@ -67,10 +67,15 @@
GENERATE_IPS_INDEX = IMG_PARAMS + "/generate_ips_search_index"
BOOT_ROOT_CONTENTS = IMG_PARAMS + "/bootroot_contents"
BOOT_ROOT_CONTENTS_BASE_INCLUDE = BOOT_ROOT_CONTENTS + "/base_include"
+BOOT_ROOT_CONTENTS_BASE_INCLUDE_NOCOMPRESS = \
+ BOOT_ROOT_CONTENTS_BASE_INCLUDE + "[fiocompress=\"false\"]"
BOOT_ROOT_CONTENTS_BASE_EXCLUDE = BOOT_ROOT_CONTENTS + "/base_exclude"
-BOOT_ROOT_CONTENTS_BASE_INCLUDE_TO_TYPE_DIR = BOOT_ROOT_CONTENTS_BASE_INCLUDE + "[type=\"dir\"]"
-BOOT_ROOT_CONTENTS_BASE_EXCLUDE_TO_TYPE_DIR = BOOT_ROOT_CONTENTS_BASE_EXCLUDE + "[type=\"dir\"]"
-BOOT_ROOT_CONTENTS_BASE_INCLUDE_TO_TYPE_FILE = BOOT_ROOT_CONTENTS_BASE_INCLUDE + "[type=\"file\"]"
+BOOT_ROOT_CONTENTS_BASE_INCLUDE_TO_TYPE_DIR = \
+ BOOT_ROOT_CONTENTS_BASE_INCLUDE + "[type=\"dir\"]"
+BOOT_ROOT_CONTENTS_BASE_EXCLUDE_TO_TYPE_DIR = \
+ BOOT_ROOT_CONTENTS_BASE_EXCLUDE + "[type=\"dir\"]"
+BOOT_ROOT_CONTENTS_BASE_INCLUDE_TO_TYPE_FILE = \
+ BOOT_ROOT_CONTENTS_BASE_INCLUDE + "[type=\"file\"]"
OUTPUT_IMAGE = IMG_PARAMS + "/output_image"
OUTPUT_IMAGE_BOOTROOT = OUTPUT_IMAGE + "/bootroot"
BOOT_ROOT_COMPRESSION_TYPE = OUTPUT_IMAGE_BOOTROOT + "/compression/type"
@@ -85,12 +90,16 @@
FINALIZER_SCRIPT = OUTPUT_IMAGE + "/finalizer/script"
FINALIZER_SCRIPT_NAME = FINALIZER_SCRIPT + "/name"
FINALIZER_SCRIPT_ARGS = FINALIZER_SCRIPT + "/argslist"
-FINALIZER_SCRIPT_NAME_TO_CHECKPOINT_MESSAGE = FINALIZER_SCRIPT + "[name=\"%s\"]/checkpoint/message"
-FINALIZER_SCRIPT_NAME_TO_CHECKPOINT_NAME = FINALIZER_SCRIPT + "[name=\"%s\"]/checkpoint/name"
+FINALIZER_SCRIPT_NAME_TO_CHECKPOINT_MESSAGE = \
+ FINALIZER_SCRIPT + "[name=\"%s\"]/checkpoint/message"
+FINALIZER_SCRIPT_NAME_TO_CHECKPOINT_NAME = \
+ FINALIZER_SCRIPT + "[name=\"%s\"]/checkpoint/name"
ADD_AUTH_URL_TO_AUTHNAME = ADD_AUTH_MAIN + "[url=\"%s\"]/authname"
ADD_AUTH_URL_TO_MIRROR_URL = ADD_AUTH_MAIN + "[url=\"%s\"]/../mirror/url"
-POST_INSTALL_ADD_URL_TO_AUTHNAME = POST_INSTALL_ADD_AUTH_MAIN + "[url=\"%s\"]/authname"
-POST_INSTALL_ADD_URL_TO_MIRROR_URL = POST_INSTALL_ADD_AUTH_MAIN + "[url=\"%s\"]/../mirror/url"
+POST_INSTALL_ADD_URL_TO_AUTHNAME = \
+ POST_INSTALL_ADD_AUTH_MAIN + "[url=\"%s\"]/authname"
+POST_INSTALL_ADD_URL_TO_MIRROR_URL = \
+ POST_INSTALL_ADD_AUTH_MAIN + "[url=\"%s\"]/../mirror/url"
FINALIZER_SCRIPT_NAME_TO_ARGSLIST = FINALIZER_SCRIPT + "[name=\"%s\"]/argslist"
# Grub menu stuff
--- a/usr/src/cmd/distro_const/auto_install/ai_sparc_image.xml Thu Jan 08 13:23:22 2009 -0700
+++ b/usr/src/cmd/distro_const/auto_install/ai_sparc_image.xml Thu Jan 08 17:06:12 2009 -0800
@@ -538,6 +538,8 @@
<base_include type="dir">var/pkg/catalog</base_include>
<base_include type="file">var/pkg/cfg_cache</base_include>
<base_include type="dir">etc</base_include>
+ <base_include type="file" fiocompress="false">etc/svc/repository.db</base_include>
+ <base_include type="file" fiocompress="false">etc/dev/.devfsadm_dev.lock</base_include>
<base_exclude type="dir">etc/gconf</base_exclude>
<base_exclude type="dir">etc/brltty</base_exclude>
<base_exclude type="dir">etc/gtk-2.0</base_exclude>
--- a/usr/src/cmd/distro_const/utils/bootroot_archive.py Thu Jan 08 13:23:22 2009 -0700
+++ b/usr/src/cmd/distro_const/utils/bootroot_archive.py Thu Jan 08 17:06:12 2009 -0800
@@ -19,7 +19,7 @@
#
# CDDL HEADER END
#
-# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
@@ -36,14 +36,18 @@
import platform
from subprocess import Popen, PIPE
from osol_install.ManifestRead import ManifestRead
+from osol_install.install_utils import find
from osol_install.distro_const.DC_ti import ti_create_target
from osol_install.distro_const.DC_ti import ti_release_target
from osol_install.distro_const.dc_utils import get_manifest_value
+from osol_install.distro_const.dc_utils import get_manifest_list
from osol_install.distro_const.DC_defs import BOOT_ROOT_COMPRESSION_LEVEL
from osol_install.distro_const.DC_defs import BOOT_ROOT_COMPRESSION_TYPE
from osol_install.distro_const.DC_defs import BOOT_ROOT_SIZE_PAD
from osol_install.distro_const.DC_defs import BR_X86_FILENAME
from osol_install.distro_const.DC_defs import BR_SPARC_FILENAME
+from osol_install.distro_const.DC_defs import \
+ BOOT_ROOT_CONTENTS_BASE_INCLUDE_NOCOMPRESS
execfile('/usr/lib/python2.4/vendor-packages/osol_install/ti_defs.py')
@@ -59,7 +63,7 @@
FIOCOMPRESS = "/usr/sbin/fiocompress"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-def compress(src,dst):
+def compress(src, dst):
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
""" fiocompress files in the dst. The files listed in
boot/solaris/filelist.ramdisk and files in usr/kernel are recopied
@@ -76,72 +80,118 @@
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
os.chdir(src)
- cmd = FIND + " . > " + TMP_DIR + "/compress_flist"
- status = os.system(cmd)
- if (status != 0):
- raise Exception, (sys.argv[0] + ":Error executing find in " + \
- src + " find command returns %d" % status)
- filelist = open(TMP_DIR + "/compress_flist", 'r')
- for file in filelist:
+ compress_flist = find(["."])
+ errors = False
+ for file in compress_flist:
# strip off the leading ./ and the trailing \n
cpio_file = file.lstrip("./").strip()
if os.access(cpio_file, os.F_OK):
# copy all files over to preserve hard links
- cmd = "echo " + cpio_file + " | " + CPIO + " -pdum " + dst + \
- " 2> /dev/null"
+ cmd = "echo " + cpio_file + " | " + CPIO + \
+ " -pdum " + dst + " 2> /dev/null"
status = os.system(cmd)
if (status != 0):
- break
+ print >>sys.stderr, (sys.argv[0] + ": cpio " +
+ "error copying file " + cpio_file +
+ " to bootroot: " + os.strerror(status >> 8))
+ errors = True
- # If the file is a regular file, has size > 0 and isn't a link,
- # fiocompress it.
- stat_out = os.stat(cpio_file)
+ # Compress the file if it is a regular file w/ size > 0
+ stat_out = os.lstat(cpio_file)
mode = stat_out.st_mode
- if stat.S_ISREG(mode) and not stat_out.st_size == 0 and \
- not os.path.islink(cpio_file):
- cmd = FIOCOMPRESS + " -mc " + cpio_file + " " + dst + \
- "/" + cpio_file
+ if (stat.S_ISREG(mode) and not (stat_out.st_size == 0)):
+ cmd = FIOCOMPRESS + " -mc " + cpio_file + \
+ " " + dst + "/" + cpio_file
status = os.system(cmd)
if (status != 0):
- break
+ print >>sys.stderr, (sys.argv[0] +
+ ": error compressing file " +
+ cpio_file + ": " +
+ os.strerror(status >> 8))
+ errors = True
+ if (errors):
+ raise Exception, (sys.argv[0] + ": Error processing " +
+ "compressed bootroot files")
+ # Re-copy a couple of files we don't want compressed.
+ # Start with the files/dirs in filelist.ramdisk, and append usr/kernel
+ rdfd = open("boot/solaris/filelist.ramdisk", 'r')
+ uc_list = []
+ for filename in rdfd:
+ uc_list.append(filename.strip())
+ rdfd.close()
+ uc_list.append("usr/kernel")
+
+ # Get expanded uncompressed filelist
+ exp_uc_list = find(uc_list)
- filelist.close()
- try:
- os.remove(TMP_DIR + "/compress_flist")
- except:
- pass
+ # Add (regular) files specified in manifest with fiocompress="false"
+ # Verify that they are non-zero-length, regular files first.
+ manflist = get_manifest_list(manifest_reader_obj,
+ BOOT_ROOT_CONTENTS_BASE_INCLUDE_NOCOMPRESS)
+ if (len(manflist) > 0):
+ status = 0
+ for nc_file in manflist:
+ try:
+ stat_out = os.lstat(nc_file)
+ except OSError, e:
+ print >>sys.stderr, (sys.argv[0] +
+ ": Couldn't stat %s to mark as " +
+ "uncompressed in bootroot: %s") % (
+ nc_file, e.strerror)
+ status = 1
+ continue
+ mode = stat_out.st_mode
+ if (stat.S_ISREG(mode) and
+ not (stat_out.st_size == 0)):
+ exp_uc_list.append(nc_file)
+ else:
+ print >>sys.stderr, (sys.argv[0] + ": " +
+ "Couldn't mark " + nc_file +
+ " as uncompressed in bootroot: " +
+ "not a non-zero-sized regular file")
+ status = 1
+ if (status != 0):
+ raise Exception, (sys.argv[0] + ": Error building "
+ "list of uncompressed bootroot files.")
+
+ # List is now built; now copy the files.
+ for file in exp_uc_list:
+ cpio_file = file.strip()
+ cmd = "echo " + cpio_file + " | cpio -pdum " + dst + \
+ " 2> /dev/null"
+ status = os.system(cmd)
+ if (status != 0):
+ print >>sys.stderr, (sys.argv[0] +
+ ": Error recopying uncompressed file " +
+ cpio_file + ": " + os.strerror(status >> 8))
+ # Don't skip out on bad status here.
+ # Try whole list before bombing out.
if (status != 0):
- raise Exception, (sys.argv[0] + ": Error executing cpio " + \
- " cpio command returns %d" % status)
+ raise Exception, (sys.argv[0] +
+ ": Error recopying uncompressed files to bootroot")
+
- # re-copy a couple of files we don't want compressed.
- cmd = FIND + \
- " `cat boot/solaris/filelist.ramdisk` -type file -print 2> /dev/null > " + \
- TMP_DIR + "/uncompress_flist"
-
- status = os.system(cmd)
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def release_archive():
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ """ Release archive.
- cmd = FIND + " usr/kernel -type file -print 2> /dev/null >> " + \
- TMP_DIR + "/uncompress_flist"
- status = os.system(cmd)
+ Args: None
- flist = open(TMP_DIR + "/uncompress_flist")
- for file in flist:
- cpio_file = file.strip()
- cmd = "echo " + cpio_file + " | cpio -pdum " + dst + " 2> /dev/null"
- status = os.system(cmd)
- if (status != 0):
- flist.close()
- os.remove(TMP_DIR + "/uncompress_flist")
- raise Exception, (sys.argv[0] + ": Error executing cpio " + \
- " cpio command returns %d" % status)
+ Returns: Status of ti_release_target()
- flist.close()
- os.remove(TMP_DIR + "/uncompress_flist")
-
+ Raises: N/A
+ """
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ return (ti_release_target({
+ TI_ATTR_TARGET_TYPE:TI_TARGET_TYPE_DC_RAMDISK,
+ TI_ATTR_DC_RAMDISK_DEST: BR_LOFI_MNT_PT,
+ TI_ATTR_DC_RAMDISK_FS_TYPE: TI_DC_RAMDISK_FS_TYPE_UFS,
+ TI_ATTR_DC_RAMDISK_BOOTARCH_NAME: BR_ARCHFILE }))
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def create_target_intr_handler(signum, frame):
@@ -159,17 +209,13 @@
"""
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
print "^C detected. Cleaning up..."
- ti_release_target({
- TI_ATTR_TARGET_TYPE:TI_TARGET_TYPE_DC_RAMDISK,
- TI_ATTR_DC_RAMDISK_DEST: BR_LOFI_MNT_PT,
- TI_ATTR_DC_RAMDISK_FS_TYPE: TI_DC_RAMDISK_FS_TYPE_UFS,
- TI_ATTR_DC_RAMDISK_BOOTARCH_NAME: BR_ARCHFILE })
+ release_archive()
sys.exit(0)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Main
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-""" Release the bootroot mount and archive the bootroot area.
+""" Archive the bootroot area.
Args:
MFEST_SOCKET: Socket needed to get manifest data via ManifestRead object
@@ -262,14 +308,10 @@
TI_ATTR_DC_RAMDISK_BOOTARCH_NAME: BR_ARCHFILE })
signal.signal (signal.SIGINT, signal.SIG_DFL)
if (status != 0):
- ti_release_target({
- TI_ATTR_TARGET_TYPE:TI_TARGET_TYPE_DC_RAMDISK,
- TI_ATTR_DC_RAMDISK_DEST: BR_LOFI_MNT_PT,
- TI_ATTR_DC_RAMDISK_FS_TYPE: TI_DC_RAMDISK_FS_TYPE_UFS,
- TI_ATTR_DC_RAMDISK_BOOTARCH_NAME: BR_ARCHFILE })
+ release_archive()
raise Exception, (sys.argv[0] +
- ": Unable to create boot archive: ti_create_target returned %d" %
- status)
+ ": Unable to create boot archive: ti_create_target returned: " +
+ os.strerror(status))
# Allow all space to be used.
# Saving 10% space as typical on UFS buys nothing for a ramdisk.
@@ -277,7 +319,8 @@
copy_status = os.system(cmd)
if (copy_status != 0): # Print a warning and forge ahead anyway...
print >>sys.stderr, (
- "Warning: Could not tunefs the bootroot to use all space")
+ "Warning: Could not tunefs the bootroot to use all space:" +
+ os.strerror(copy_status >> 8))
if is_sparc:
etc_system = open(BR_BUILD + "/etc/system", "a+")
@@ -290,28 +333,25 @@
cmd += FIND + " . | " + CPIO + " -pdum " + BR_LOFI_MNT_PT
copy_status = os.system(cmd)
if (copy_status != 0):
- status = ti_release_target({
- TI_ATTR_TARGET_TYPE:TI_TARGET_TYPE_DC_RAMDISK,
- TI_ATTR_DC_RAMDISK_DEST: BR_LOFI_MNT_PT,
- TI_ATTR_DC_RAMDISK_FS_TYPE: TI_DC_RAMDISK_FS_TYPE_UFS,
- TI_ATTR_DC_RAMDISK_BOOTARCH_NAME: BR_ARCHFILE })
+ release_archive()
raise Exception, (sys.argv[0] + ": Error copying files to bootroot " +
- "container; find/cpio command returns %d" % status)
+ "container; find/cpio command returns: " +
+ os.strerror(copy_status >> 8))
if is_sparc:
print "Doing compression..."
- compress(BR_BUILD, BR_LOFI_MNT_PT)
+ try:
+ compress(BR_BUILD, BR_LOFI_MNT_PT)
+ except Exception, e:
+ release_archive()
+ raise
# Unmount the bootroot file and delete the lofi device
-status = ti_release_target({
- TI_ATTR_TARGET_TYPE:TI_TARGET_TYPE_DC_RAMDISK,
- TI_ATTR_DC_RAMDISK_DEST: BR_LOFI_MNT_PT,
- TI_ATTR_DC_RAMDISK_FS_TYPE: TI_DC_RAMDISK_FS_TYPE_UFS,
- TI_ATTR_DC_RAMDISK_BOOTARCH_NAME: BR_ARCHFILE })
+status = release_archive()
if (status != 0):
raise Exception, (sys.argv[0] +
- ": Unable to release boot archive: ti_release_target returned %d" %
- status)
+ ": Unable to release boot archive: ti_release_target returned: " +
+ os.strerror(status))
# We did the sparc compression above, now do it for x86
if not is_sparc:
@@ -325,22 +365,24 @@
if (BR_COMPR_TYPE == "gzip"):
cmd += "-tgzip -mx=" + BR_COMPR_LEVEL + " "
else:
- raise Exception, (sys.argv[0] + ": Unrecognized bootroot " +
+ raise Exception, (sys.argv[0] + \
+ ": Unrecognized bootroot " +
"compression type: " + BR_COMPR_TYPE)
cmd += BR_ARCHFILE + ".gz " + BR_ARCHFILE
status = os.system(cmd)
if (status != 0):
raise Exception, (sys.argv[0] +
": Error compressing bootroot: " +
- "7za command returns %d" % status)
+ "7za command returns: " + os.strerror(status >> 8))
# move compressed file to proper location in pkg image area
mvcmd = MV + " " + BR_ARCHFILE + ".gz " + BR_ARCHFILE
status = os.system(mvcmd)
if (status != 0):
raise Exception, (sys.argv[0] + ": Error moving " +
- "bootroot from %s to %s: mv returns %d" %
- (BR_ARCHFILE + '.gz', BR_ARCHFILE, status))
+ "bootroot from %s to %s: %s" %
+ (BR_ARCHFILE + '.gz', BR_ARCHFILE,
+ os.strerror(status >> 8)))
os.chmod(BR_ARCHFILE, 0644)
--- a/usr/src/lib/install_utils/install_utils.py Thu Jan 08 13:23:22 2009 -0700
+++ b/usr/src/lib/install_utils/install_utils.py Thu Jan 08 17:06:12 2009 -0800
@@ -18,12 +18,13 @@
#
# CDDL HEADER END
#
-# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
import sys
import os
+import stat
import errno
import select
from subprocess import *
@@ -371,7 +372,6 @@
IOError with errno = EINVAL: mode argument is invalid
"""
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-def canaccess(filename, mode):
# Don't try opening file with mode "w", else file will be truncated.
if (mode == "rw"):
@@ -452,3 +452,112 @@
log.log(stderr_log_level, output)
return (p.wait())
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def __find_error_handler(raise_me):
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ """Error handler for find() below. Raises the exception passed to it.
+
+ Args:
+ raise_me: Exception to raise
+
+ Returns: None
+
+ Raises: The exception passed in.
+ """
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ raise raise_me
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def find(rootpaths, type=None, raise_errors=False):
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ """Python implementation of the Unix find(1) command.
+
+ Order of output is similar, but not the same as, find(1) when -depth
+ is not passed to it. A directory is enumerated before the files it
+ contains.
+
+ Unlike find(1), no errors are returned when given a file or directory
+ to parse which doesn't exist (when raise_errors is False)
+
+ Args:
+ rootpaths: list of file and/or directory pathnames to parse.
+
+ type:
+ - When set to None, return all found pathnames.
+ - When set to "dir", return all found directories only. This
+ does not include links to directories.
+ - When set to "file", return all found files only. This does
+ not include links to files.
+
+ raise_errors:
+ - When set to True: raise an OSError when a non-existant file
+ or directory to parse is passed in the rootpaths list.
+ - When set to False, ignore any such errors.
+ """
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ rlist = []
+ error_handler = None
+
+ if ((type != None) and (type != "dir") and (type != "file")):
+ raise Exception, (
+ "find: \"type\" must be None, \"dir\" or \"file\"")
+
+ if (raise_errors):
+ error_handler = __find_error_handler
+
+ for rootpath in rootpaths:
+
+ try:
+ stat_out = os.lstat(rootpath)
+ except OSError: # Doesn't exist
+ continue
+
+ # Handle case where rootpath is not a proper directory.
+ # (Links to directories are not proper directories.)
+ if ((type == None) and (not stat.S_ISDIR(stat_out.st_mode))):
+ rlist.append(rootpath)
+ continue
+
+ # Print rootpath only if a proper file, if type == "file"
+ elif (type == "file"):
+ if (stat.S_ISREG(stat_out.st_mode)):
+ rlist.append(rootpath)
+ continue
+
+ for path, subdirs, files in os.walk(rootpath, True,
+ error_handler):
+
+ # Take the path if we're taking everything, or if
+ # type == dir and path is a proper directory (not link)
+ if ((type == None) or
+ ((type == "dir") and
+ (stat.S_ISDIR(stat_out.st_mode)))):
+ rlist.append(path)
+
+ # Only the directory is desired.
+ if (type == "dir"):
+ continue
+
+ # If names listed in subdirs are not directories (e.g.
+ # they are symlinks), append them if type is None,
+ # like what find(1) does. If they are dirs, then
+ # os.walk will get them on the next pass.
+ if (type == None):
+ for subdir in subdirs:
+ fullname = path + "/" + subdir
+ stat_out = os.lstat(fullname)
+ if (not stat.S_ISDIR(stat_out.st_mode)):
+ rlist.append(fullname)
+
+ for file in files:
+ fullname = path + "/" + file
+ # Check for a proper file if (type == "file")
+ if (type == "file"):
+ stat_out = os.lstat(fullname)
+ if (not stat.S_ISREG(stat_out.st_mode)):
+ continue
+ rlist.append(fullname)
+ return rlist