From 1bd2040a698ab5d6035aaf13c3544e5ab3c8af2c Mon Sep 17 00:00:00 2001 From: Apple Date: Wed, 13 May 2009 00:39:33 +0000 Subject: [PATCH] libutil-20.tar.gz --- ExtentManager.cpp | 171 +++++++++++++++++++++++ ExtentManager.h | 74 ++++++++++ Makefile | 32 +++-- libutil.exports | 26 ++++ libutil.h | 5 +- reexec_to_match_kernel.3 | 30 ++++ reexec_to_match_kernel.c | 174 +++++++++++++++++++++++ wipefs.3 | 134 ++++++++++++++++++ wipefs.cpp | 290 +++++++++++++++++++++++++++++++++++++++ wipefs.h | 47 +++++++ 10 files changed, 969 insertions(+), 14 deletions(-) create mode 100644 ExtentManager.cpp create mode 100644 ExtentManager.h create mode 100644 libutil.exports create mode 100644 reexec_to_match_kernel.3 create mode 100644 reexec_to_match_kernel.c create mode 100644 wipefs.3 create mode 100644 wipefs.cpp create mode 100644 wipefs.h diff --git a/ExtentManager.cpp b/ExtentManager.cpp new file mode 100644 index 0000000..74d54c6 --- /dev/null +++ b/ExtentManager.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +// +// ExtentManager.cpp +// + +#include "ExtentManager.h" + +void +ExtentManager::Init(uint32_t theBlockSize, uint32_t theNativeBlockSize, off_t theTotalBytes) +{ + blockSize = theBlockSize; + nativeBlockSize = theNativeBlockSize; + totalBytes = theTotalBytes; + totalBlocks = howmany(totalBytes, blockSize); + + // add sentry empty extents at both sides so empty partition doesn't need to be handled specially + AddBlockRangeExtent(0, 0); + AddBlockRangeExtent(totalBlocks, 0); +} + +void +ExtentManager::MergeExtent(const ExtentInfo &a, const ExtentInfo &b, ExtentInfo *c) +{ + // merge ext into *curIt + c->blockAddr = min(a.blockAddr, b.blockAddr); + c->numBlocks = max(a.blockAddr + a.numBlocks, b.blockAddr + b.numBlocks) - c->blockAddr; +} + +void +ExtentManager::AddBlockRangeExtent(off_t blockAddr, off_t numBlocks) +{ + struct ExtentInfo ext, newExt; + ListExtIt curIt, newIt; + bool merged = false; + + // make the range a valid range + if ((blockAddr > totalBlocks) || (blockAddr + numBlocks < 0)) { // totally out of range, do nothing + return; + } + if (blockAddr < 0) { + numBlocks = blockAddr + numBlocks; + blockAddr = 0; + } + if (blockAddr + numBlocks > totalBlocks) { + numBlocks = totalBlocks - blockAddr; + } + + ext.blockAddr = blockAddr; + ext.numBlocks = numBlocks; + + for (curIt = extentList.begin(); curIt != extentList.end(); curIt++) { + if (BeforeExtent(ext, *curIt)) + break; + if (!BeforeExtent(*curIt, ext)) { // overlapped extents + MergeExtent(ext, *curIt, &newExt); + *curIt = newExt; + merged = true; + break; + } + } + + // insert ext before curIt + if (!merged) { + curIt = extentList.insert(curIt, ext); // throws bad_alloc when out of memory + } + + // merge the extents + newIt = curIt; + curIt = extentList.begin(); + while (curIt != extentList.end()) { + if (curIt == newIt || BeforeExtent(*curIt, *newIt)) { // curIt is before newIt + curIt++; + continue; + } + if (BeforeExtent(*newIt, *curIt)) { // curIt is after newIt now, we are done + break; + } + // merge the two extents + MergeExtent(*curIt, *newIt, &newExt); + *newIt = newExt; + curIt = extentList.erase(curIt); + } + // printf("After %s(%lld, %lld)\n", __func__, blockAddr, numBlocks); DebugPrint(); +} // ExtentManager::AddBlockRangeExtent + +void +ExtentManager::RemoveBlockRangeExtent(off_t blockAddr, off_t numBlocks) +{ + struct ExtentInfo ext, newExt; + ListExtIt curIt; + + ext.blockAddr = blockAddr; + ext.numBlocks = numBlocks; + + curIt = extentList.begin(); + while (curIt != extentList.end()) { + if (BeforeExtent(*curIt, ext)) { + curIt++; + continue; + } + if (BeforeExtent(ext, *curIt)) // we are done + break; + // overlapped extents + if (curIt->blockAddr >= ext.blockAddr && + curIt->blockAddr + curIt->numBlocks <= ext.blockAddr + ext.numBlocks) { + // *curIt is totally within ext, remove curIt + curIt = extentList.erase(curIt); + } else if (curIt->blockAddr < ext.blockAddr && + curIt->blockAddr + curIt->numBlocks > ext.blockAddr + ext.numBlocks) { + // ext is totally within *curIt, split ext into two + newExt.blockAddr = ext.blockAddr + ext.numBlocks; + newExt.numBlocks = curIt->blockAddr + curIt->numBlocks - newExt.blockAddr; + curIt->numBlocks = ext.blockAddr - curIt->blockAddr; + curIt++; + extentList.insert(curIt, newExt); // throws bad_alloc when out of memory + curIt++; + } else { // remove part of ext + if (curIt->blockAddr >= ext.blockAddr) { // remove the former part of *curIt + assert(curIt->blockAddr + curIt->numBlocks > newExt.blockAddr); + newExt.blockAddr = ext.blockAddr + ext.numBlocks; + newExt.numBlocks = curIt->blockAddr + curIt->numBlocks - newExt.blockAddr; + *curIt = newExt; + } else { // remove the latter part of *curIt + curIt->numBlocks = ext.blockAddr - curIt->blockAddr; + } + curIt++; + } + } + //printf("After %s(%lld, %lld)\n", __func__, blockAddr, numBlocks); DebugPrint(); +} + +void +ExtentManager::AddByteRangeExtent(off_t byteAddr, off_t numBytes) +{ + off_t blockAddr = byteAddr / blockSize; + off_t blockAddrOfLastByte = (byteAddr + numBytes - 1) / blockSize; + off_t numBlocks = blockAddrOfLastByte - blockAddr + 1; + AddBlockRangeExtent(blockAddr, numBlocks); +} + +void +ExtentManager::DebugPrint() +{ + ListExtIt it; + + for (it = extentList.begin(); it != extentList.end(); it++) { + printf("[%lld, %lld] ", it->blockAddr, it->numBlocks); + } + printf("\n"); +} diff --git a/ExtentManager.h b/ExtentManager.h new file mode 100644 index 0000000..2dc25f6 --- /dev/null +++ b/ExtentManager.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2008 Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +// +// ExtentManager.h +// +#ifndef EXTENTMANAGER_H +#define EXTENTMANAGER_H + +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +struct ExtentInfo { + off_t blockAddr; + off_t numBlocks; +}; + +inline bool BeforeExtent(const ExtentInfo &a, const ExtentInfo &b) +{ + return (a.blockAddr + a.numBlocks) < b.blockAddr; +} + +typedef list::iterator ListExtIt; + +class ExtentManager { +public: + ExtentManager() : blockSize(0), totalBytes(0), totalBlocks(0) {}; + ~ExtentManager() {}; + + void Init(uint32_t theBlockSize, uint32_t theNativeBlockSize, off_t theTotalBytes); + + void AddBlockRangeExtent(off_t blockAddr, off_t numBlocks); + void AddByteRangeExtent(off_t byteAddr, off_t numBytes); + void RemoveBlockRangeExtent(off_t blockAddr, off_t numBlocks); + + void DebugPrint(); + +protected: + void MergeExtent(const ExtentInfo &a, const ExtentInfo &b, ExtentInfo *c); + +public: + size_t blockSize; + size_t nativeBlockSize; + off_t totalBytes; + off_t totalBlocks; + list extentList; +}; + +#endif // #ifndef EXTENTMANAGER_H diff --git a/Makefile b/Makefile index 19c1c9b..67173c8 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,18 @@ SHELL := /bin/sh +SDKROOT ?= / VERSION = 1.0 -CC = cc +CC = xcrun cc +CPP = xcrun c++ CPPFLAGS = -I$(SRCROOT) -CFLAGS = -Os -g3 -no-cpp-precomp -Wall $(RC_CFLAGS) +CFLAGS = -Os -g3 -no-cpp-precomp -Wall $(RC_CFLAGS) -isysroot $(SDKROOT) LDFLAGS = $(RC_CFLAGS) -install_name /usr/lib/libutil.dylib -compatibility_version $(VERSION) \ - -current_version $(VERSION) + -current_version $(VERSION) -lstdc++ -exported_symbols_list libutil.exports -isysroot $(SDKROOT) INSTALL = install -c LN = ln -MKDIR = mkdir +MKDIR = mkdir -p STRIP = strip +DSYMUTIL = dsymutil AR = ar RANLIB = ranlib @@ -20,13 +23,15 @@ DSTROOT = LIB := libutil1.0.dylib SRCS := _secure_path.c getmntopts.c humanize_number.c \ - pidfile.c property.c realhostname.c trimdomain.c uucplock.c -HDRS := libutil.h mntopts.h + pidfile.c property.c realhostname.c trimdomain.c uucplock.c \ + ExtentManager.cpp wipefs.cpp reexec_to_match_kernel.c +HDRS := libutil.h mntopts.h wipefs.h MAN3 := _secure_path.3 getmntopts.3 humanize_number.3 pidfile.3 \ - property.3 realhostname.3 realhostname_sa.3 trimdomain.3 uucplock.3 + property.3 realhostname.3 realhostname_sa.3 trimdomain.3 \ + uucplock.3 wipefs.3 reexec_to_match_kernel.3 .SUFFIXES : -.SUFFIXES : .c .h .o +.SUFFIXES : .c .cpp .h .o .PHONY : .PHONY : all installsrc installhdrs install clean installlib installman @@ -57,7 +62,7 @@ installhdrs : install : installhdrs installlib strip installman install-plist clean : - rm -f $(patsubst %.c,$(OBJROOT)/%.o,$(SRCS)) + rm -f $(patsubst %.cpp,$(OBJROOT)/%.o,$(patsubst %.c,$(OBJROOT)/%.o,$(SRCS))) rm -f $(SYMROOT)/*~ rm -f $(SRCROOT)/.\#* rm -f $(SYMROOT)/$(LIB) @@ -69,6 +74,7 @@ strip: # Internal targets and rules. # installlib : $(SYMROOT)/$(LIB) + $(DSYMUTIL) $(SYMROOT)/$(LIB) -o $(SYMROOT)/$(LIB).dSYM $(INSTALL) -d $(DSTROOT)/usr/lib $(INSTALL) -m 0755 $< $(DSTROOT)/usr/lib $(LN) -fs libutil1.0.dylib $(DSTROOT)/usr/lib/libutil.dylib @@ -84,8 +90,12 @@ $(OBJROOT)/%.o : $(SRCROOT)/%.c \ $(patsubst %.h,$(SRCROOT)/%.h,$(HDRS)) $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ -$(SYMROOT)/$(LIB) : $(patsubst %.c,$(OBJROOT)/%.o,$(SRCS)) - $(CC) -dynamiclib $(LDFLAGS) -o $@ $? +$(OBJROOT)/%.o : $(SRCROOT)/%.cpp \ + $(patsubst %.h,$(SRCROOT)/%.h,$(HDRS)) + $(CPP) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ + +$(SYMROOT)/$(LIB) : $(patsubst %.cpp,$(OBJROOT)/%.o,$(patsubst %.c,$(OBJROOT)/%.o,$(SRCS))) + $(CC) -dynamiclib $(LDFLAGS) -o $@ $(patsubst %.cpp,$(OBJROOT)/%.o,$(patsubst %.c,$(OBJROOT)/%.o,$(SRCS))) OSV = $(DSTROOT)/usr/local/OpenSourceVersions OSL = $(DSTROOT)/usr/local/OpenSourceLicenses diff --git a/libutil.exports b/libutil.exports new file mode 100644 index 0000000..5da5c6f --- /dev/null +++ b/libutil.exports @@ -0,0 +1,26 @@ +__secure_path +_freemntopts +_getmnt_silent +_getmntoptnum +_getmntopts +_getmntoptstr +_humanize_number +_pidfile_close +_pidfile_open +_pidfile_remove +_pidfile_write +_properties_free +_properties_read +_property_find +_realhostname +_realhostname_sa +_reexec_to_match_kernel +_trimdomain +_uu_lock +_uu_lock_txfr +_uu_lockerr +_uu_unlock +_wipefs_alloc +_wipefs_except_blocks +_wipefs_free +_wipefs_wipe diff --git a/libutil.h b/libutil.h index 7135673..c1a1bcd 100644 --- a/libutil.h +++ b/libutil.h @@ -96,9 +96,6 @@ struct sockaddr; int realhostname_sa(char *host, size_t hsize, struct sockaddr *addr, int addrlen); -int kld_isloaded(const char *name); -int kld_load(const char *name); - #ifdef _STDIO_H_ /* avoid adding new includes */ char *fparseln(FILE *, size_t *, size_t *, const char[3], int); #endif @@ -125,6 +122,8 @@ int pidfile_close(struct pidfh *pfh); int pidfile_remove(struct pidfh *pfh); #endif +int reexec_to_match_kernel(void); + __END_DECLS #define UU_LOCK_INUSE (1) diff --git a/reexec_to_match_kernel.3 b/reexec_to_match_kernel.3 new file mode 100644 index 0000000..df1c77d --- /dev/null +++ b/reexec_to_match_kernel.3 @@ -0,0 +1,30 @@ +.Dd Apr 14, 2008 +.Dt REEXEC_TO_MATCH_KERNEL 3 +.Os "Mac OS X" +.Sh NAME +.Nm reexec_to_match_kernel +.Nd Re-exec the current binary to match the ABI of the running kernel +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In libutil.h +.Ft int +.Fo reexec_to_match_kernel +.Fa "void" +.Fc +.Sh DESCRIPTION +The +.Fn reexec_to_match_kernel +function re-executes the current binary to match the ABI of the running kernel. +That is, if the current kernel is a 64-bit Intel kernel, it will attempt to +execute the 64-bit x86_64 userspace slice of the universal binary. The API +intentionally does not take arguments because its use should be transparent +to the program and to the user. +.Sh RETURN VALUES +The +.Fn reexec_to_match_kernel +function returns 0 if re-execution was not required. It returns -1 and +sets errno if there was an error performing the re-execution, for example +if the binary is not universal, or does not contain a slice to match the running +kernel's ABI. If the function succeeds, control never returns to the caller +and the program starts from main() again. diff --git a/reexec_to_match_kernel.c b/reexec_to_match_kernel.c new file mode 100644 index 0000000..a617c64 --- /dev/null +++ b/reexec_to_match_kernel.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libutil.h" + +static cpu_type_t current_program_arch(void); +static cpu_type_t current_kernel_arch(void); +static int reexec(cpu_type_t cputype); + +#define kReExec "REEXEC_TO_MATCH_KERNEL" + +int reexec_to_match_kernel(void) +{ + cpu_type_t kernarch, progarch; + char *alreadyenv; + + alreadyenv = getenv(kReExec); + if (alreadyenv) { + /* we've done this at least once, assume + another try won't help */ + return 0; + } + + kernarch = current_kernel_arch(); + progarch = current_program_arch(); + + if (kernarch == 0) { + /* could not determine kernel arch */ + errno = EINVAL; + return -1; + } + + if (kernarch == progarch) { + /* nothing to do here */ + return 0; + } + + /* Now we need to re-exec */ + return reexec(kernarch); +} + +static cpu_type_t current_program_arch(void) +{ + cpu_type_t current_arch = (_NSGetMachExecuteHeader())->cputype; + + return current_arch; +} + +static cpu_type_t current_kernel_arch(void) +{ + struct host_basic_info hi; + unsigned int size; + kern_return_t kret; + cpu_type_t current_arch; + int ret, mib[4]; + size_t len; + struct kinfo_proc kp; + + size = sizeof(hi)/sizeof(int); + kret = host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)&hi, &size); + if (kret != KERN_SUCCESS) { + return 0; + } + + current_arch = hi.cpu_type; + + /* Now determine if the kernel is running in 64-bit mode */ + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = 0; /* kernproc, pid 0 */ + len = sizeof(kp); + ret = sysctl(mib, sizeof(mib)/sizeof(mib[0]), &kp, &len, NULL, 0); + if (ret == -1) { + return 0; + } + + if (kp.kp_proc.p_flag & P_LP64) { + current_arch |= CPU_ARCH_ABI64; + } + + return current_arch; +} + +static int reexec(cpu_type_t cputype) +{ + posix_spawnattr_t attr; + int ret, envcount; + size_t copied = 0; + char **argv, **oldenvp, **newenvp; + char execpath[MAXPATHLEN+1]; + uint32_t execsize; + + argv = *_NSGetArgv(); + oldenvp = *_NSGetEnviron(); + for (envcount = 0; oldenvp[envcount]; envcount++); + // if there are 4 elements and a NULL, envcount will be 4 + + newenvp = calloc(envcount+2, sizeof(newenvp[0])); + for (envcount = 0; oldenvp[envcount]; envcount++) { + newenvp[envcount] = oldenvp[envcount]; + } + newenvp[envcount++] = kReExec"=1"; + newenvp[envcount] = NULL; + + execsize = (uint32_t)sizeof(execpath); + ret = _NSGetExecutablePath(execpath, &execsize); + if (ret != 0) { + return -1; + } + + ret = posix_spawnattr_init(&attr); + if (ret != 0) { + return -1; + } + ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC); + if (ret != 0) { + return -1; + } + ret = posix_spawnattr_setbinpref_np(&attr, 1, &cputype, &copied); + if (ret != 0 || copied != 1) { + return -1; + } + + /* + fprintf(stderr, "reexec: %s\n", execpath); + for (envcount=0; newenvp[envcount]; envcount++) { + fprintf(stderr, "env[%d] = %s\n", envcount, newenvp[envcount]); + } + for (envcount=0; argv[envcount]; envcount++) { + fprintf(stderr, "argv[%d] = %s\n", envcount, argv[envcount]); + } + */ + ret = posix_spawn(NULL, execpath, NULL, &attr, argv, newenvp); + if (ret != 0) { + errno = ret; + return -1; + } + + /* should not be reached */ + return 0; +} diff --git a/wipefs.3 b/wipefs.3 new file mode 100644 index 0000000..0f14dfc --- /dev/null +++ b/wipefs.3 @@ -0,0 +1,134 @@ +.\" +.\" Copyright (c) 2008 Apple Inc. All rights reserved. +.\" +.\" @APPLE_LICENSE_HEADER_START@ +.\" +.\" This file contains Original Code and/or Modifications of Original Code +.\" as defined in and that are subject to the Apple Public Source License +.\" Version 2.0 (the 'License'). You may not use this file except in +.\" compliance with the License. Please obtain a copy of the License at +.\" http://www.opensource.apple.com/apsl/ and read it before using this +.\" file. +.\" +.\" The Original Code and all software distributed under the License are +.\" distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +.\" EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +.\" INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +.\" FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +.\" Please see the License for the specific language governing rights and +.\" limitations under the License. +.\" +.\" @APPLE_LICENSE_HEADER_END@ +.\" +.Dd 2/25/08 \" DATE +.Dt libutil 3 \" Program name and manual section number +.Os Mac OS X +.Sh NAME \" Section Header - required - don't modify +.\" The following lines are read in generating the apropos(man -k) database. Use only key +.\" words here as the database is built based on the words here and in the .ND line. +.Nm wipefs_alloc , +.Nm wipefs_except_blocks , +.Nm wipefs_wipe , +.Nm wipefs_free +.\" Use .Nm macro to designate other names for the documented program. +.Nd wipes existing file systems on a volume +.Sh LIBRARY \" Section Header - required - don't modify +.Lb libutil +.Sh SYNOPSIS +.In wipefs.h +.Ft int +.Fo wipefs_alloc +.Fa "int file_desc" +.Fa "size_t block_size" +.Fa "wipefs_ctx *handlep" +.Fc +.Ft int +.Fo wipefs_except_blocks +.Fa "wipefs_ctx handle" +.Fa "off_t blockoff" +.Fa "off_t nblocks" +.Fc +.Ft int +.Fo wipefs_wipe +.Fa "wipefs_ctx handle" +.Fc +.Ft void +.Fo wipefs_free +.Fa "wipefs_ctx *handlep" +.Fc +.Sh DESCRIPTION \" Section Header - required - don't modify +The wipefs family of functions wipe existing file systems on a volume. This is usually used by the newfs_* utilities before they create new file systems on the volume, so that the existing file system will not be mounted accidentally after the new file system is created. +.Pp +The +.Fn wipefs_alloc +function initializes a +.Fa wipefs_ctx +object (which is an opaque data type). +.Fa file_desc +is the file handle of the volume to be wiped, which can be a block device node, a character device node, or a file. +.Fa file_desc +must be opened with write access. If +.Fa block_size +is 0, this function calls +.Xr ioctl 2 +to get the block size. A valid +.Fa block_size +must be supplied if +.Fa file_desc +is a regular file. This function does not write any data to the volume. +.Pp +The +.Fn wipefs_except_blocks +function tells wipefs not to write anything in the block range provided. This function is used for performance +optimizations if the caller will write to these blocks. It is the caller's responsibility to write to these blocks. +Otherwise, some file systems may still be recognized on the volume. This function does not write any data to the +volume. If this function is called multiple times, the union of all the ranges provided will be excluded from being +written by +.Fn wipefs_wipe . +.Pp +The +.Fn wipefs_wipe +function writes data to the volume to wipe out existing file systems on it. +.Cm Caution: +this function destroys any file system or partition scheme on the volume represented by +.Fa file_desc . +If +.Fa file_desc +represents the entire disk (e.g. /dev/diskX), the partition map of the disk will be destroyed. If +.Fa file_desc +represents a partition (e.g., /dev/diskXsY), only the file system in that partition is destroyed. Although the partition scheme or file system on +.Fa file_desc +may be beyond repair after +.Fn wipefs_wipe , +this function is not designed as a means to safely delete all data. It is possible that some user data (or intact file systems in some partitions) may still be recovered. +.Pp +The +.Fn wipefs_free +function frees the allocated +.Fa wipefs_ctx +handle and set +.Fa *handlep +to NULL. +.Sh RETURN VALUES +The +.Fn wipefs_alloc , +.Fn wipefs_except_blocks +and +.Fn wipefs_wipe +functions return 0 on success, or will fail and return an error code. +Each function may return +.Fa ENOMEM +if insufficient memory is available. In addition, if +.Fa block_size +is not provided, +.Fn wipefs_alloc +may return any error +.Xr ioctl 2 +returns; +.Fn wipefs_wipe +may return any error +.Xr pwrite 2 +returns. +.\" .Sh BUGS \" Document known, unremedied bugs +.\".Sh HISTORY \" Document history if command behaves in a unique manner +.\"The wipefs family of functions first appeared in Mac OS X Leopard (10.5.3). diff --git a/wipefs.cpp b/wipefs.cpp new file mode 100644 index 0000000..d5efc05 --- /dev/null +++ b/wipefs.cpp @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +// +// wipefs.cpp +// + +#include +#include +#include +#include +#include + +#include "ExtentManager.h" +#include "wipefs.h" + +#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) + +struct __wipefs_ctx { + int fd; + class ExtentManager extMan; +}; + +void +AddExtentsForFutureFS(class ExtentManager *extMan) +{ + // we don't know what blocks future FS will use to recognize itself. But we'd better be safe than sorry and write + // the first and last 2MB of the volume + off_t size = 2 * 1024 * 1024; + extMan->AddByteRangeExtent(0, size); + extMan->AddByteRangeExtent(extMan->totalBytes - size, size); +} + +void +AddExtentsForHFS(class ExtentManager *extMan) +{ + // first 1KB is boot block, last 512B is reserved + // the Volume Header (512B) is after 1KB and before the last 512B + extMan->AddByteRangeExtent(0, 1024 + 512); + extMan->AddByteRangeExtent(extMan->totalBytes - 1024, 1024); +} + +void +AddExtentsForMSDOS(class ExtentManager *extMan) +{ + // MSDOS needs the first block (in theory, up to 32KB) + extMan->AddByteRangeExtent(0, 32 * 1024); +} + +void +AddExtentsForNTFS(class ExtentManager *extMan) +{ + // NTFS supports block size from 256B to 32768B. The first, middle and last block are needed + extMan->AddByteRangeExtent(0, 32 * 1024); + extMan->AddByteRangeExtent(extMan->totalBytes - 32 * 1024, 32 * 1024); + // to be safe, add the rage from (mid_point - 32KB) to (mid_point + 32KB) + extMan->AddByteRangeExtent(extMan->totalBytes / 2 - 32 * 1024, 64 * 1024); +} + +void +AddExtentsForUDF(class ExtentManager *extMan) +{ + off_t lastBlockAddr = extMan->totalBlocks - 1; + + // Volume Recognization Sequence (VRS) starts at 32KB, usually less than 7 Volume Structure Descriptors (2KB each) + extMan->AddByteRangeExtent(32 * 1024, 14 * 1024); + + // AVDP is on 256, 512, last block, last block - 256 + extMan->AddBlockRangeExtent(256, 1); + extMan->AddBlockRangeExtent(512, 1); + extMan->AddBlockRangeExtent(lastBlockAddr, 1); + extMan->AddBlockRangeExtent(lastBlockAddr - 256, 1); + + // to be safe, assume the device has 2KB block size and do it again + if (extMan->blockSize != 2048) { + off_t blockSize = 2048; + // AVDP is on 256, 512, last block, last block - 256 + extMan->AddByteRangeExtent(256 * blockSize, blockSize); + extMan->AddByteRangeExtent(512 * blockSize, blockSize); + extMan->AddByteRangeExtent(extMan->totalBytes - blockSize, blockSize); + extMan->AddByteRangeExtent(extMan->totalBytes - 256 * blockSize, blockSize); + } +} + +void +AddExtentsForUFS(class ExtentManager *extMan) +{ + // UFS super block is 8KB at offset 8KB + extMan->AddByteRangeExtent(8192, 8192); +} + +void +AddExtentsForZFS(class ExtentManager *extMan) +{ + // ZFS needs the first 512KB and last 512KB for all the 4 disk labels + extMan->AddByteRangeExtent(0, 512 * 1024); + extMan->AddByteRangeExtent(extMan->totalBytes - 512 * 1024, 512 * 1024); +} + +void +AddExtentsForPartitions(class ExtentManager *extMan) +{ + // MBR (Master Boot Record) needs the first sector + // APM (Apple Partition Map) needs the second sector + // GPT (GUID Partition Table) needs the first 34 and last 33 sectors + extMan->AddByteRangeExtent(0, 512 * 34); + extMan->AddByteRangeExtent(extMan->totalBytes - 512 * 33, 512 * 33); +} + +extern "C" int +wipefs_alloc(int fd, size_t block_size, wipefs_ctx *handle) +{ + int err = 0; + uint64_t numBlocks = 0; + uint32_t nativeBlockSize = 0; + off_t totalSizeInBytes = 0; + class ExtentManager *extMan = NULL; + struct stat sbuf = { 0 }; + + *handle = NULL; + (void)fstat(fd, &sbuf); + switch (sbuf.st_mode & S_IFMT) { + case S_IFCHR: + case S_IFBLK: + if (ioctl(fd, DKIOCGETBLOCKSIZE, (char *)&nativeBlockSize) < 0) { + err = errno; + goto labelExit; + } + if (ioctl(fd, DKIOCGETBLOCKCOUNT, (char *)&numBlocks) < 0) { + err = errno; + goto labelExit; + } + totalSizeInBytes = numBlocks * nativeBlockSize; + break; + case S_IFREG: + nativeBlockSize = sbuf.st_blksize; + numBlocks = sbuf.st_size / sbuf.st_blksize; + totalSizeInBytes = sbuf.st_size; + break; + default: + errno = EINVAL; + goto labelExit; + } + if (block_size == 0) { + block_size = nativeBlockSize; + } + if (block_size == 0 || totalSizeInBytes == 0) { + err = EINVAL; + goto labelExit; + } + + try { + *handle = new __wipefs_ctx; + if (*handle == NULL) { + bad_alloc e; + throw e; + } + + (*handle)->fd = fd; + extMan = &(*handle)->extMan; + + extMan->Init(block_size, nativeBlockSize, totalSizeInBytes); + AddExtentsForFutureFS(extMan); + AddExtentsForHFS(extMan); + AddExtentsForMSDOS(extMan); + AddExtentsForNTFS(extMan); + AddExtentsForUDF(extMan); + AddExtentsForUFS(extMan); + AddExtentsForZFS(extMan); + AddExtentsForPartitions(extMan); + } + catch (bad_alloc &e) { + err = ENOMEM; + } + catch (...) { // currently only ENOMEM is possible + err = ENOMEM; + } + + labelExit: + if (err != 0) { + wipefs_free(handle); + } + return err; +} // wipefs_alloc + +extern "C" int +wipefs_except_blocks(wipefs_ctx handle, off_t block_offset, off_t nblocks) +{ + int err = 0; + try { + handle->extMan.RemoveBlockRangeExtent(block_offset, nblocks); + } + catch (bad_alloc &e) { + err = ENOMEM; + } + catch (...) { // currently only ENOMEM is possible + err = ENOMEM; + } + return err; +} + +extern "C" int +wipefs_wipe(wipefs_ctx handle) +{ + int err = 0; + uint8_t *bufZero = NULL; + ListExtIt curExt; + size_t bufSize; + dk_discard_t discard; + + memset(&discard, 0, sizeof(dk_discard_t)); + discard.length = handle->extMan.totalBytes; + + // + // Don't bother to check the return value since this is mostly + // informational for the lower-level drivers. + // + ioctl(handle->fd, DKIOCDISCARD, (caddr_t)&discard); + + + bufSize = 256 * 1024; // issue large I/O to get better performance + bufZero = new uint8_t[bufSize]; + bzero(bufZero, bufSize); + + off_t byteOffset, totalBytes; + size_t numBytes, numBytesToWrite, blockSize; + + blockSize = handle->extMan.blockSize; + totalBytes = handle->extMan.totalBytes; + // write zero to all extents + for (curExt = handle->extMan.extentList.begin(); curExt != handle->extMan.extentList.end(); curExt++) { + byteOffset = curExt->blockAddr * blockSize; + numBytes = curExt->numBlocks * blockSize; + // make both offset and numBytes on native block boundary + if (byteOffset % handle->extMan.nativeBlockSize != 0 || + numBytes % handle->extMan.nativeBlockSize != 0) { + size_t nativeBlockSize = handle->extMan.nativeBlockSize; + off_t newOffset, newEndOffset; + newOffset = byteOffset / nativeBlockSize * nativeBlockSize; + newEndOffset = roundup(byteOffset + numBytes, nativeBlockSize); + byteOffset = newOffset; + numBytes = newEndOffset - newOffset; + } + if (byteOffset + (off_t)numBytes > totalBytes) { + numBytes = totalBytes - byteOffset; + } + while (numBytes > 0) { + numBytesToWrite = min(numBytes, bufSize); + if (pwrite(handle->fd, bufZero, numBytesToWrite, byteOffset) != (ssize_t)numBytesToWrite) { + err = errno; + goto labelExit; + } + numBytes -= numBytesToWrite; + byteOffset += numBytesToWrite; + } + } + + labelExit: + if (bufZero != NULL) + delete[] bufZero; + return err; +} // wipefs_wipe + +extern "C" void +wipefs_free(wipefs_ctx *handle) +{ + if (*handle != NULL) { + delete *handle; + *handle = NULL; + } +} diff --git a/wipefs.h b/wipefs.h new file mode 100644 index 0000000..e615d5e --- /dev/null +++ b/wipefs.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +// +// wipefs.h +// +#ifndef WIPEFS_H +#define WIPEFS_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct __wipefs_ctx *wipefs_ctx; + +__BEGIN_DECLS +extern int wipefs_alloc(int fd, size_t block_size, wipefs_ctx *handle); +extern int wipefs_except_blocks(wipefs_ctx handle, off_t block_offset, off_t nblocks); +extern int wipefs_wipe(wipefs_ctx handle); +extern void wipefs_free(wipefs_ctx *handle); +__END_DECLS + +#ifdef __cplusplus +} +#endif + +#endif // #ifndef WIPEFS_H + -- 2.47.2