diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 562d7463c..db1c4d2df 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -62,6 +62,7 @@ add_subdirectory(libunwind) add_subdirectory(libsystem) add_subdirectory(libutil) add_subdirectory(libnotify) +add_subdirectory(libremovefile) add_subdirectory(launchd/liblaunch) add_subdirectory(keymgr) add_subdirectory(libiconv) diff --git a/src/libremovefile/CMakeLists.txt b/src/libremovefile/CMakeLists.txt new file mode 100644 index 000000000..96219fb98 --- /dev/null +++ b/src/libremovefile/CMakeLists.txt @@ -0,0 +1,28 @@ +project(libremovefile) + +cmake_minimum_required(VERSION 2.4.0) + +include_directories(${DARLING_TOP_DIRECTORY}/platform-include) +include_directories(${DARLING_TOP_DIRECTORY}/src/libc/gen) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fblocks -nostdinc") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib -Wl,--version-script=${DARLING_TOP_DIRECTORY}/darwin.map") + +set(removefile_sources + removefile.c + removefile_random.c + removefile_rename_unlink.c + removefile_sunlink.c + removefile_tree_walker.c +) + +SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib${SUFFIX}/darling") +SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) +SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + +add_library(removefile SHARED ${removefile_sources}) +target_link_libraries(removefile system_c system_kernel) + +install(TARGETS removefile DESTINATION lib${SUFFIX}/darling) + diff --git a/src/libremovefile/LICENSE b/src/libremovefile/LICENSE new file mode 100644 index 000000000..c0281e09d --- /dev/null +++ b/src/libremovefile/LICENSE @@ -0,0 +1,26 @@ +Terms and Conditions + + Copyright � 2000 Matthew D. Gauthier + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the contributors shall + not be used in advertising or otherwise to promote the sale, use or + other dealings in this Software without prior written authorization. diff --git a/src/libremovefile/checkint.3 b/src/libremovefile/checkint.3 new file mode 100644 index 000000000..5e4ad750e --- /dev/null +++ b/src/libremovefile/checkint.3 @@ -0,0 +1,204 @@ +.Dd April 20, 2007 +.Dt CHECK_INT32_ADD 3 +.Os +.Sh NAME +.Nm check_int32_add , +.Nm check_uint32_add , +.Nm check_int64_add , +.Nm check_uint64_add , +.Nm check_int32_sub , +.Nm check_uint32_sub , +.Nm check_int64_sub , +.Nm check_uint64_sub , +.Nm check_int32_mul , +.Nm check_uint32_mul , +.Nm check_int64_mul , +.Nm check_uint64_mul , +.Nm check_int32_div , +.Nm check_uint32_div , +.Nm check_int64_div , +.Nm check_uint64_div , +.Nd detect overflow in arithmetic +.Sh SYNOPSIS +.In checkint.h +.Ft int32_t +.Fo check_int32_add +.Fa "int x" +.Fa "int y" +.Fa "int *err" +.Fc +.Ft uint32_t +.Fo check_uint32_add +.Fa "int x" +.Fa "int y" +.Fa "int *err" +.Fc +.Ft int64_t +.Fo check_int64_add +.Fa "int x" +.Fa "int y" +.Fa "int *err" +.Fc +.Ft uint64_t +.Fo check_uint64_add +.Fa "int x" +.Fa "int y" +.Fa "int *err" +.Fc +.Ft int32_t +.Fo check_int32_sub +.Fa "int x" +.Fa "int y" +.Fa "int *err" +.Fc +.Ft uint32_t +.Fo check_uint32_sub +.Fa "int x" +.Fa "int y" +.Fa "int *err" +.Fc +.Ft int64_t +.Fo check_int64_sub +.Fa "int x" +.Fa "int y" +.Fa "int *err" +.Fc +.Ft uint64_t +.Fo check_uint64_sub +.Fa "int x" +.Fa "int y" +.Fa "int *err" +.Fc +.Ft int32_t +.Fo check_int32_mul +.Fa "int x" +.Fa "int y" +.Fa "int *err" +.Fc +.Ft uint32_t +.Fo check_uint32_mul +.Fa "int x" +.Fa "int y" +.Fa "int *err" +.Fc +.Ft int64_t +.Fo check_int64_mul +.Fa "int x" +.Fa "int y" +.Fa "int *err" +.Fc +.Ft uint64_t +.Fo check_uint64_mul +.Fa "int x" +.Fa "int y" +.Fa "int *err" +.Fc +.Ft int32_t +.Fo check_int32_div +.Fa "int x" +.Fa "int y" +.Fa "int *err" +.Fc +.Ft uint32_t +.Fo check_uint32_div +.Fa "int x" +.Fa "int y" +.Fa "int *err" +.Fc +.Ft int64_t +.Fo check_int64_div +.Fa "int x" +.Fa "int y" +.Fa "int *err" +.Fc +.Ft uint64_t +.Fo check_uint64_div +.Fa "int x" +.Fa "int y" +.Fa "int *err" +.Fc +.Sh DESCRIPTION +The +.Fn check__ "x" "y" "err" +family of functions perform the specified arithmetic operation (addition, subtraction, +multiplication, or division) with the left operand of +.Fa x +and right operand of +.Fa y +and return the arithmetic result with the specified type. +.Pp +Either operand +.Fa x +or +.Fa y +(or both) can be of any type that is compatible to signed or unsigned +8-bit, 16-bit, 32-bit, or 64-bit integers. +.Pp +The +.Fa err +argument is +.Em or Ns 'ed +by flags in the function to indicate if an overflow has occurred. +The possible flag values are: +.Pp +.Bd -literal -offset indent -compact +CHECKINT_NO_ERROR no overflow has occurred +CHECKINT_OVERFLOW_ERROR overflow has occurred +CHECKINT_TYPE_ERROR operand is of an incompatible type +.Ed +.Pp +The +.Fa err +argument is not cleared in calls to the +.Fn check__ "x" "y" "err" +functions. Detected overflow persists in the +.Fa err +argument until +.Fa err +is reset to CHECKINT_NO_ERROR. +.Sh RETURN VALUES +If successful, the +.Fn check__ +functions will return the arithmetic result of performing the operation with left operand +.Fa x +and right operand +.Fa y +(even when overflow error occurs). +.Pp +If any other error occurs, the return value is -1 +and the argument +.Fa err +will be set to indicate the error. +.Sh EXAMPLES +.Bd -literal -offset indent +/* Create a variable to store overflow flag */ +int32_t err = CHECKINT_NO_ERROR; +/* Use checkint API to perform an arithmetic operation and + * store result in variable. */ +int32_t arithmetic_result = check_int32_add(UINT_MAX, 1, &err); +/* Check status of overflow flag */ +if (err & CHECKINT_OVERFLOW_ERROR) { + /* Perform overflow resolution code */ + fprintf(stderr, "Overflow detected!\\n"); +} +/* Check for type error */ +else if (err & CHECKINT_TYPE_ERROR) { + /* Deal with incompatible types error */ + fprintf(stderr, "Incompatible types!\\n"); +} +/* Reset overflow flag for next operation */ +err = CHECKINT_NO_ERROR; + +.Ed +.Sh ERRORS +The +.Fn check__ +functions may fail if: +.Pp +.Bd -literal -offset indent -compact +[CHECKINT_TYPE_ERROR] operand is of an incompatible type +.Ed +.Sh HISTORY +The +.Fn checkint +API was introduced in Mac OS X 10.5. diff --git a/src/libremovefile/checkint.h b/src/libremovefile/checkint.h new file mode 100644 index 000000000..2cbbcb964 --- /dev/null +++ b/src/libremovefile/checkint.h @@ -0,0 +1,561 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + */ + +#ifndef __CHECKINT_H__ +#define __CHECKINT_H__ + +/* ObjC++ Guard */ +#if defined(__OBJC__) && defined(__cplusplus) +#error "checkint.h does not support Objective C++" +#elif defined(__cplusplus) +#error "checkint.h does not support C++" +#endif + +#include +#include + +__BEGIN_DECLS + +enum { + CHECKINT_NO_ERROR = 0, + CHECKINT_OVERFLOW_ERROR = (1 << 0), + CHECKINT_TYPE_ERROR = (1 << 1) +}; + +#define check_int32_add(x, y, err) __checkint_int32_add(x,y,err) +#define check_uint32_add(x, y, err) __checkint_uint32_add(x,y,err) +#define check_int64_add(x, y, err) __checkint_int64_add(x,y,err) +#define check_uint64_add(x, y, err) __checkint_uint64_add(x,y,err) + +#define check_int32_sub(x, y, err) __checkint_int32_sub(x,y,err) +#define check_uint32_sub(x, y, err) __checkint_uint32_sub(x,y,err) +#define check_int64_sub(x, y, err) __checkint_int64_sub(x,y,err) +#define check_uint64_sub(x, y, err) __checkint_uint64_sub(x,y,err) + +#define check_int32_mul(x, y, err) __checkint_int32_mul(x,y,err) +#define check_uint32_mul(x, y, err) __checkint_uint32_mul(x,y,err) +#define check_int64_mul(x, y, err) __checkint_int64_mul(x,y,err) +#define check_uint64_mul(x, y, err) __checkint_uint64_mul(x,y,err) + +#define check_int32_div(x, y, err) __checkint_int32_div(x,y,err) +#define check_uint32_div(x, y, err) __checkint_uint32_div(x,y,err) +#define check_int64_div(x, y, err) __checkint_int64_div(x,y,err) +#define check_uint64_div(x, y, err) __checkint_uint64_div(x,y,err) + +/*** + * Private Interfaces + * + * Please do not directly use any interfaces below this point. They are + * considered an implementation detail of the above, supported, interfaces + * and are subject to change at any time without warning. + ***/ + +#define __CHECKINT_INLINE static inline __attribute__((always_inline)) + +__CHECKINT_INLINE int32_t +__checkint_is_mixed_sign32(int32_t x, int32_t y) {return ((x ^ y) < 0);} + +__CHECKINT_INLINE int32_t +__checkint_is_mixed_sign64(int64_t x, int64_t y) {return ((x ^ y) < 0);} + +__CHECKINT_INLINE int32_t +__checkint_int32_type_error(int32_t* err) {*err |= CHECKINT_TYPE_ERROR; return -1;} + +__CHECKINT_INLINE int32_t +__checkint_uint32_type_error(int32_t* err) {*err |= CHECKINT_TYPE_ERROR; return -1;} + +__CHECKINT_INLINE int32_t +__checkint_int64_type_error(int32_t* err) {*err |= CHECKINT_TYPE_ERROR; return -1;} + +__CHECKINT_INLINE int32_t +__checkint_uint64_type_error(int32_t* err) {*err |= CHECKINT_TYPE_ERROR; return -1;} + +__CHECKINT_INLINE int32_t +__checkint_int32_add(int64_t x, int64_t y, int32_t* err) { + int64_t z = x + y; + if (x < INT32_MIN || x > INT32_MAX || y < INT32_MIN || y > INT32_MAX) { + *err |= CHECKINT_OVERFLOW_ERROR; + } + if (z > INT32_MAX || z < INT32_MIN) *err |= CHECKINT_OVERFLOW_ERROR; + return (int32_t)z; +} + +__CHECKINT_INLINE uint32_t +__checkint_uint32_add(int64_t x, int64_t y, int32_t* err) { + int64_t z = x + y; + if ((x & 0xffffffff00000000ull) || (y & 0xffffffff00000000ull)) *err |= CHECKINT_OVERFLOW_ERROR; + if (z > UINT_MAX || z < 0) *err |= CHECKINT_OVERFLOW_ERROR; + return (uint32_t)z; +} + +__CHECKINT_INLINE int64_t +__checkint_int64_add_signed_signed(int64_t x, int64_t y, int32_t* err) { + /* Mixed-sign additions cannot overflow */ + if (__checkint_is_mixed_sign64(x,y)) { + /* else, both arguments negative */ + } else if (y < 0) { + if (x < LLONG_MIN - y) *err |= CHECKINT_OVERFLOW_ERROR; + /* else, both arguments positive */ + } else { + if (LLONG_MAX - x < y) *err |= CHECKINT_OVERFLOW_ERROR; + } + return x + y; +} + +__CHECKINT_INLINE int64_t +__checkint_int64_add_signed_unsigned(int64_t x, uint64_t y, int32_t* err) { + if(((int64_t)(LLONG_MAX - y)) < x) + *err = *err | CHECKINT_OVERFLOW_ERROR; + return x + y; +} + +__CHECKINT_INLINE int64_t +__checkint_int64_add_unsigned_signed(uint64_t x, int64_t y, int32_t* err) { + return __checkint_int64_add_signed_unsigned(y, x, err); +} + +__CHECKINT_INLINE int64_t +__checkint_int64_add_unsigned_unsigned(uint64_t x, uint64_t y, int32_t* err) { + int64_t diff = LLONG_MAX - y; + if(diff < 0 || ((uint64_t) diff) < x) + *err = *err | CHECKINT_OVERFLOW_ERROR; + return x + y; +} + +__CHECKINT_INLINE uint64_t +__checkint_uint64_add_unsigned_unsigned(uint64_t x, uint64_t y, int32_t* err) { + if((ULLONG_MAX - y) < x) + *err = *err | CHECKINT_OVERFLOW_ERROR; + return x + y; +} + +__CHECKINT_INLINE uint64_t +__checkint_uint64_add_signed_signed(int64_t x, int64_t y, int32_t* err) { + if(((x < 0 && y >= 0) || (x >= 0 && y < 0)) && (x + y) < 0) + *err = *err | CHECKINT_OVERFLOW_ERROR; + else if(x < 0 && y < 0) + *err = *err | CHECKINT_OVERFLOW_ERROR; + return x + y; +} + +__CHECKINT_INLINE uint64_t +__checkint_uint64_add_signed_unsigned(int64_t x, uint64_t y, int32_t* err) { + if(x > 0) + return __checkint_uint64_add_unsigned_unsigned(x, y, err); + if((y < ((uint64_t)LLONG_MAX + 1)) && (((int64_t) (x + y)) < 0)) + *err = *err | CHECKINT_OVERFLOW_ERROR; + return x + y; +} + +__CHECKINT_INLINE uint64_t +__checkint_uint64_add_unsigned_signed(uint64_t x, int64_t y, int32_t* err) { + return __checkint_uint64_add_signed_unsigned(y, x, err); +} + +__CHECKINT_INLINE int32_t +__checkint_int32_sub(int64_t x, int64_t y, int32_t* err) { + if (x < INT32_MIN || x > INT32_MAX || y < INT32_MIN || y > INT32_MAX) { + *err |= CHECKINT_OVERFLOW_ERROR; + } + int64_t z = x - y; + if (z > INT_MAX || z < INT_MIN) *err |= CHECKINT_OVERFLOW_ERROR; + return (int32_t)z; +} + +__CHECKINT_INLINE uint32_t +__checkint_uint32_sub(int64_t x, int64_t y, int32_t* err) { + int64_t z = x - y; + if ((x & 0xffffffff00000000ull) || (y & 0xffffffff00000000ull)) *err |= CHECKINT_OVERFLOW_ERROR; + if (z > UINT_MAX || z < 0) *err |= CHECKINT_OVERFLOW_ERROR; + return (uint32_t)z; +} + +__CHECKINT_INLINE int64_t +__checkint_int64_sub_signed_signed(int64_t x, int64_t y, int32_t* err) { + if(__checkint_is_mixed_sign64(x, y)) + { + /* Positive x subtract a negative y */ + if(x >= 0) + { + if(x > LLONG_MAX + y) + *err = *err | CHECKINT_OVERFLOW_ERROR; + } + /* Negative x subtract a positive y */ + else + { + if(x < LLONG_MIN + y) + *err = *err | CHECKINT_OVERFLOW_ERROR; + } + } + /* Both negative, or both positive, no possible overflow */ + return x - y; +} + +__CHECKINT_INLINE int64_t +__checkint_int64_sub_signed_unsigned(int64_t x, uint64_t y, int32_t* err) { + if(x < ((int64_t)(LLONG_MIN + y))) + *err = *err | CHECKINT_OVERFLOW_ERROR; + return x - y; +} + +__CHECKINT_INLINE int64_t +__checkint_int64_sub_unsigned_signed(uint64_t x, int64_t y, int32_t* err) { + if(x > ((uint64_t)(LLONG_MAX + y)) || y == LLONG_MIN) + *err = *err | CHECKINT_OVERFLOW_ERROR; + return x - y; +} + +__CHECKINT_INLINE int64_t +__checkint_int64_sub_unsigned_unsigned(uint64_t x, uint64_t y, int32_t* err) { + if(x > y && ((x - y) > LLONG_MAX)) + *err = *err | CHECKINT_OVERFLOW_ERROR; + else if(x < y && ((y - x - 1) > LLONG_MAX)) + *err = *err | CHECKINT_OVERFLOW_ERROR; + return x - y; +} + +__CHECKINT_INLINE uint64_t +__checkint_uint64_sub_signed_signed(int64_t x, int64_t y, int32_t* err) { + if(((x < 0 && y <= 0) || (x >= 0 && y > 0)) && (x - y) < 0) + *err = *err | CHECKINT_OVERFLOW_ERROR; + else if(x < 0 && y > 0) + *err = *err | CHECKINT_OVERFLOW_ERROR; + return x - y; +} + +__CHECKINT_INLINE uint64_t +__checkint_uint64_sub_signed_unsigned(int64_t x, uint64_t y, int32_t* err) { + if(y > ((uint64_t) LLONG_MAX + 1) || ((int64_t) y) > x) + *err = *err | CHECKINT_OVERFLOW_ERROR; + return x - y; +} + +__CHECKINT_INLINE uint64_t +__checkint_uint64_sub_unsigned_signed(uint64_t x, int64_t y, int32_t* err) { + if(x <= LLONG_MAX) + return __checkint_uint64_sub_signed_signed(x, y, err); + else if (y == LLONG_MIN || -y > ULLONG_MAX - x) + *err = *err | CHECKINT_OVERFLOW_ERROR; + return x - y; +} + +__CHECKINT_INLINE uint64_t +__checkint_uint64_sub_unsigned_unsigned(uint64_t x, uint64_t y, int32_t* err) { + if(x < y) + *err = *err | CHECKINT_OVERFLOW_ERROR; + return x - y; +} + +__CHECKINT_INLINE int32_t +__checkint_int32_mul(int64_t x, int64_t y, int32_t* err) { + int64_t z = x * y; + if (x < INT32_MIN || x > INT32_MAX || y < INT32_MIN || y > INT32_MAX) { + *err |= CHECKINT_OVERFLOW_ERROR; + } + if (z > INT_MAX || z < INT_MIN) *err |= CHECKINT_OVERFLOW_ERROR; + return (int32_t)z; +} + +__CHECKINT_INLINE uint32_t +__checkint_uint32_mul(int64_t x, int64_t y, int32_t* err) { + int64_t z = x * y; + if ((x & 0xffffffff00000000ull) || (y & 0xffffffff00000000ull)) *err |= CHECKINT_OVERFLOW_ERROR; + if (z > UINT_MAX || z < 0) *err |= CHECKINT_OVERFLOW_ERROR; + return (uint32_t)z; +} + +__CHECKINT_INLINE int64_t +__checkint_int64_mul_signed_signed(int64_t x, int64_t y, int32_t* err) { + if(x == 0 || y == 0) return 0; + + if(!__checkint_is_mixed_sign64(x, y)) + { + if(x > 0) + { + if(LLONG_MAX/x < y) + *err = *err | CHECKINT_OVERFLOW_ERROR; + } + else + { + if(x == LLONG_MIN || y == LLONG_MIN) + *err = *err | CHECKINT_OVERFLOW_ERROR; + if(LLONG_MAX/(-x) < (-y)) + *err = *err | CHECKINT_OVERFLOW_ERROR; + } + } + else + { + if(x < 0) + { + if(x < LLONG_MIN/y) + *err = *err | CHECKINT_OVERFLOW_ERROR; + } + else + if(y < LLONG_MIN/x) + *err = *err | CHECKINT_OVERFLOW_ERROR; + } + return x * y; +} + +__CHECKINT_INLINE uint64_t +__checkint_uint64_mul_unsigned_unsigned(uint64_t x, uint64_t y, int32_t* err) { + if(x == 0) return 0; + + if(ULLONG_MAX/x < y) + *err = *err | CHECKINT_OVERFLOW_ERROR; + return x * y; +} + + +__CHECKINT_INLINE int64_t +__checkint_int64_mul_unsigned_unsigned(uint64_t x, uint64_t y, int32_t* err) { + if(x == 0) return 0; + + if(LLONG_MAX/x < y) + *err = *err | CHECKINT_OVERFLOW_ERROR; + return x * y; +} + +__CHECKINT_INLINE int64_t +__checkint_int64_mul_signed_unsigned(int64_t x, uint64_t y, int32_t* err) { + if(y == 0) return 0; + + if(x >= 0) + return __checkint_int64_mul_unsigned_unsigned(x, y, err); + else + if(x < LLONG_MIN/y || x > LLONG_MAX/y) + *err = *err | CHECKINT_OVERFLOW_ERROR; + return x * y; +} + +__CHECKINT_INLINE int64_t +__checkint_int64_mul_unsigned_signed(uint64_t x, int64_t y, int32_t* err) { + return __checkint_int64_mul_signed_unsigned(y, x, err); +} + +__CHECKINT_INLINE uint64_t +__checkint_uint64_mul_signed_signed(int64_t x, int64_t y, int32_t* err) { + if((x < 0 && y > 0) || (x > 0 && y < 0)) + *err = *err | CHECKINT_OVERFLOW_ERROR; + else if(x > 0 && y > 0) + return __checkint_uint64_mul_unsigned_unsigned(x, y, err); + else + return __checkint_uint64_mul_unsigned_unsigned(-x, -y, err); + return x * y; +} + +__CHECKINT_INLINE uint64_t +__checkint_uint64_mul_signed_unsigned(int64_t x, uint64_t y, int32_t* err) { + if(x >= 0) + return __checkint_uint64_mul_unsigned_unsigned(x, y, err); + *err = *err | CHECKINT_OVERFLOW_ERROR; + return (uint64_t) (x * y); +} + +__CHECKINT_INLINE uint64_t +__checkint_uint64_mul_unsigned_signed(uint64_t x, int64_t y, int32_t* err) { + return __checkint_uint64_mul_signed_unsigned(y, x, err); +} + +__CHECKINT_INLINE int32_t +__checkint_int32_div_signed_signed(int32_t x, int32_t y, int32_t* err) { + if((x == INT_MIN) && y == -1) + *err = *err | CHECKINT_OVERFLOW_ERROR; + return x / y; +} + +__CHECKINT_INLINE int32_t +__checkint_int32_div_signed_unsigned(int32_t x, uint32_t y, int32_t* err) { + if(y <= INT_MAX) + return x / (int32_t) y; + return 0; +} + +__CHECKINT_INLINE int32_t +__checkint_int32_div_unsigned_signed(uint32_t x, int32_t y, int32_t* err) { + if(x == ((uint32_t) INT_MAX + 1) && y == -1) + return INT_MIN; + if(x > ((uint32_t) INT_MAX + 1) && y == -1) + *err = *err | CHECKINT_OVERFLOW_ERROR; + else if(x > INT_MAX && y == 1) + *err = *err | CHECKINT_OVERFLOW_ERROR; + if(x <= INT_MAX) + return ((int32_t) x) / y; + if(y > 0) + return x / y; + return -(x / (uint32_t) -y); +} + +__CHECKINT_INLINE int32_t +__checkint_int32_div_unsigned_unsigned(uint32_t x, uint32_t y, int32_t* err) { + uint32_t result = x / y; + if(result > INT_MAX) + *err = *err | CHECKINT_OVERFLOW_ERROR; + return x / y; +} + +__CHECKINT_INLINE uint32_t +__checkint_uint32_div_signed_signed(int32_t x, int32_t y, int32_t* err) { + int32_t result = x / y; + if(x == INT_MIN && y == -1) + return ((uint32_t) -x); + if(result < 0) + *err = *err | CHECKINT_OVERFLOW_ERROR; + if(x >= 0 && y > 0) + return x / y; + else if(x < 0 && y > 0) + return -((uint32_t) -x / y); + else if(x > 0 && y < 0) + return -(x / (uint32_t) -y); + else + return ((uint32_t) -x / (uint32_t) -y); +} + +__CHECKINT_INLINE uint32_t +__checkint_uint32_div_signed_unsigned(int32_t x, uint32_t y, int32_t* err) { + if(x < 0 && ((uint32_t) -x) >= y) + *err = *err | CHECKINT_OVERFLOW_ERROR; + if(x >= 0) + return x / y; + return -(((uint32_t) -x) / y); +} + +__CHECKINT_INLINE uint32_t +__checkint_uint32_div_unsigned_signed(uint32_t x, int32_t y, int32_t* err) { + if(y < 0 && ((uint32_t) -y) <= x) + *err = *err | CHECKINT_OVERFLOW_ERROR; + if(y > 0) + return x / y; + return -(x / ((uint32_t) -y)); +} + +__CHECKINT_INLINE uint32_t +__checkint_uint32_div_unsigned_unsigned(uint32_t x, uint32_t y, int32_t* err) { + return x / y; +} + +__CHECKINT_INLINE int64_t +__checkint_int64_div_signed_signed(int64_t x, int64_t y, int32_t* err) { + if((x == LLONG_MIN) && y == -1) + *err = *err | CHECKINT_OVERFLOW_ERROR; + return x / y; +} + +__CHECKINT_INLINE int64_t +__checkint_int64_div_signed_unsigned(int64_t x, uint64_t y, int32_t* err) { + if(y <= LLONG_MAX) + return x / (int64_t) y; + return 0; +} + +__CHECKINT_INLINE int64_t +__checkint_int64_div_unsigned_signed(uint64_t x, int64_t y, int32_t* err) { + if(x == ((uint64_t) LLONG_MAX + 1) && y == -1) + return LLONG_MIN; + if(x > ((uint64_t) LLONG_MAX + 1) && y == -1) + *err = *err | CHECKINT_OVERFLOW_ERROR; + else if(x > LLONG_MAX && y == 1) + *err = *err | CHECKINT_OVERFLOW_ERROR; + if(x <= LLONG_MAX) + return ((int64_t) x) / y; + if(y > 0) + return x / y; + return -(x / (uint64_t) -y); +} + +__CHECKINT_INLINE int64_t +__checkint_int64_div_unsigned_unsigned(uint64_t x, uint64_t y, int32_t* err) { + uint64_t result = x / y; + if(result > LLONG_MAX) + *err = *err | CHECKINT_OVERFLOW_ERROR; + return x / y; +} + +__CHECKINT_INLINE uint64_t +__checkint_uint64_div_signed_signed(int64_t x, int64_t y, int32_t* err) { + int64_t result = x / y; + if(x == LLONG_MIN && y == -1) + return ((uint64_t)LLONG_MAX) + 1; + if(result < 0) + *err = *err | CHECKINT_OVERFLOW_ERROR; + if(x >= 0 && y > 0) + return x / y; + else if(x < 0 && y > 0) + return -((uint64_t) -x / y); + else if(x > 0 && y < 0) + return -(x / (uint64_t) -y); + else + return ((uint64_t) -x / (uint64_t) -y); +} + +__CHECKINT_INLINE uint64_t +__checkint_uint64_div_signed_unsigned(int64_t x, uint64_t y, int32_t* err) { + if(x < 0 && ((uint64_t) -x) >= y) + *err = *err | CHECKINT_OVERFLOW_ERROR; + if(x >= 0) + return x / y; + return -(((uint64_t) -x) / y); +} + +__CHECKINT_INLINE uint64_t +__checkint_uint64_div_unsigned_signed(uint64_t x, int64_t y, int32_t* err) { + if(y < 0 && ((uint64_t) -y) <= x) + *err = *err | CHECKINT_OVERFLOW_ERROR; + if(y > 0) + return x / y; + return -(x / ((uint64_t) -y)); +} + +__CHECKINT_INLINE uint64_t +__checkint_uint64_div_unsigned_unsigned(uint64_t x, uint64_t y, int32_t* err) { + return x / y; +} + +#undef __CHECKINT_INLINE + +/******/ + +#ifdef __GNUC__ +#define __checkint_same_type(e1, e2) __builtin_types_compatible_p(__typeof__(e1), __typeof__(e2)) +#define __checkint_cond_expr(c, e1, e2) __builtin_choose_expr(c, e1, e2) +#else +#error "need compiler support for __checkint_same_type() and __checkint_cond_expr()" +#endif + +/******/ + +#define __checkint_is_signed(x) (__checkint_same_type(x, int8_t) || __checkint_same_type(x, int16_t) || __checkint_same_type(x, int32_t) || __checkint_same_type(x, int64_t) || __checkint_same_type(x, signed long)) +#define __checkint_is_unsigned(x) (__checkint_same_type(x, uint8_t) || __checkint_same_type(x, uint16_t) || __checkint_same_type(x, uint32_t) || __checkint_same_type(x, uint64_t) || __checkint_same_type(x, uintptr_t) || __checkint_same_type(x, unsigned long)) + +#define __checkint_is_signed_signed(x, y) (__checkint_is_signed(x) && __checkint_is_signed(y)) +#define __checkint_is_signed_unsigned(x, y) (__checkint_is_signed(x) && __checkint_is_unsigned(y)) +#define __checkint_is_unsigned_signed(x, y) (__checkint_is_unsigned(x) && __checkint_is_signed(y)) +#define __checkint_is_unsigned_unsigned(x, y) (__checkint_is_unsigned(x) && __checkint_is_unsigned(y)) + +/******/ + +#define __CHECKINT_SIGN_DEMUX(type,oper,x,y,err) \ + (__checkint_cond_expr(__checkint_is_signed_signed(x, y), __checkint_ ## type ## _ ## oper ## _signed_signed(x, y, err), \ + __checkint_cond_expr(__checkint_is_signed_unsigned(x, y), __checkint_ ## type ## _ ## oper ## _signed_unsigned(x, y, err), \ + __checkint_cond_expr(__checkint_is_unsigned_signed(x, y), __checkint_ ## type ## _ ## oper ## _unsigned_signed(x, y, err), \ + __checkint_cond_expr(__checkint_is_unsigned_unsigned(x, y), __checkint_ ## type ## _ ## oper ## _unsigned_unsigned(x, y, err), \ + __checkint_ ## type ## _type_error(err)))))) + +#define __checkint_int64_add(x,y,err) __CHECKINT_SIGN_DEMUX(int64,add,x,y,err) +#define __checkint_uint64_add(x,y,err) __CHECKINT_SIGN_DEMUX(uint64,add,x,y,err) + +#define __checkint_int64_sub(x,y,err) __CHECKINT_SIGN_DEMUX(int64,sub,x,y,err) +#define __checkint_uint64_sub(x,y,err) __CHECKINT_SIGN_DEMUX(uint64,sub,x,y,err) + +#define __checkint_int64_mul(x,y,err) __CHECKINT_SIGN_DEMUX(int64,mul,x,y,err) +#define __checkint_uint64_mul(x,y,err) __CHECKINT_SIGN_DEMUX(uint64,mul,x,y,err) + +#define __checkint_int32_div(x,y,err) __CHECKINT_SIGN_DEMUX(int32,div,x,y,err) +#define __checkint_int64_div(x,y,err) __CHECKINT_SIGN_DEMUX(int64,div,x,y,err) +#define __checkint_uint32_div(x,y,err) __CHECKINT_SIGN_DEMUX(uint32,div,x,y,err) +#define __checkint_uint64_div(x,y,err) __CHECKINT_SIGN_DEMUX(uint64,div,x,y,err) + +__END_DECLS + +#endif /* __CHECKINT_H__ */ diff --git a/src/libremovefile/removefile.3 b/src/libremovefile/removefile.3 new file mode 100644 index 000000000..2b24c1ee8 --- /dev/null +++ b/src/libremovefile/removefile.3 @@ -0,0 +1,284 @@ +.\" Copyright (c) 2007 Apple Inc. +.\" 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. +.\" 3. Neither the name of Apple Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. +.\" +.Dd May 4, 2007 +.Dt REMOVEFILE 3 +.Os +.Sh NAME +.Nm removefile , removefile_state_alloc , +.Nm removefile_state_free , removefile_state_get , +.Nm removefile_state_set +.Nd remove files or directories +.Sh SYNOPSIS +.In removefile.h +.Ft int +.Fn removefile "const char *path" "removefile_state_t state" "removefile_flags_t flags" +.Ft removefile_state_t +.Fn removefile_state_alloc "void" +.Ft int +.Fn removefile_state_free "removefile_state_t state" +.Ft int +.Fn removefile_state_get "removefile_state_t state" "uint32_t key" "void * dst" +.Ft int +.Fn removefile_state_set "removefile_state_t state" "uint32_t key" "const void * value" +.Ft int +.Fn removefile_cancel "removefile_state_t state" +.Sh DESCRIPTION +.Pp +These functions are used to remove a file or directory. Various levels +of overwriting may be specified to prevent other people from recovering any +information about the file. +.Pp +The +.Fn removefile_state_alloc +function initializes a +.Vt removefile_state_t +object (which is an opaque data type). +This object can be passed to +.Fn removefile . +.Fn removefile_state_get +and +.Fn removefile_state_set +can be used to manipulate the state (see below). +The +.Fn removefile_state_free +function is used to deallocate the object and its contents. +.Pp +The +.Fn removefile +function removes files and directories located at the named +.Va path +filesystem location. The named +.Va path +location can be specified as either an absolute path or relative to the working directory +of the calling process. +If the +.Va state +parameter is the return value from +.Fn removefile_state_alloc , +then +.Fn removefile +will use the information from the state object; if it is +.Dv NULL , +then +.Fn removefile +will work normally, but less control will be available to the caller. +The +.Va flags +parameter controls deletion options: +.Bl -tag -width REMOVEFILE_SECURE_1_PASS_ZERO +.It Dv REMOVEFILE_RECURSIVE +If the +.Va path +location is a directory, then recursively delete the entire directory. +.It Dv REMOVEFILE_KEEP_PARENT +The file or directory at the +.Va path +location is not deleted. If specified in conjunction with REMOVEFILE_RECURSIVE, +then all of the contents of the directory at +.Va path +location will be deleted, but not the directory itself. +.It Dv REMOVEFILE_SECURE_7_PASS +Overwrite the file with 7 US DoD compliant passes (0xF6, 0x00, 0xFF, random, 0x00, 0xFF, random). +.It Dv REMOVEFILE_SECURE_35_PASS +Overwrite the file using 35-pass Gutmann algorithm. +.It Dv REMOVEFILE_SECURE_3_PASS +Overwrite the file twice with random bytes, and then with 0xAA. +.It Dv REMOVEFILE_SECURE_1_PASS +Overwrite with a single pass of random data. +.It Dv REMOVEFILE_SECURE_1_PASS_ZERO +Overwrite with a single pass of zeroes. +.El +.Pp +The +.Fn removefile_state_get +and +.Fn removefile_state_set +functions can be used to manipulate the +.Ft removefile_state_t +object returned by +.Fn removefile_state_alloc . +In both functions, the +.Va dst +or the +.Va value +parameter's type depends on the +.Va key +parameter that is passed in. +.Bl -tag -width REMOVEFILE_STATE_CONFIRM_CALLBACK +.It Dv REMOVEFILE_STATE_CONFIRM_CALLBACK +Get or set the callback function of type +.Va removefile_callback_t +to be called prior to file deletion. +.It Dv REMOVEFILE_STATE_CONFIRM_CONTEXT +Get or set any parameters of type +.Va void * +that are needed for the confirm callback function. +.It Dv REMOVEFILE_STATE_ERROR_CALLBACK +Get or set the callback function of type +.Va removefile_callback_t +to be called when an error is detected. +.It Dv REMOVEFILE_STATE_ERROR_CONTEXT +Get or set any parameters of type +.Va void * +that are needed for the error callback function. +.It Dv REMOVEFILE_STATE_ERRNO +Get or set the current errno of type +.Va int +.It Dv REMOVEFILE_STATE_STATUS_CALLBACK +Get or set the callback function of type +.Va removefile_callback_t +to be called subsequent to file deletion. +.It Dv REMOVEFILE_STATE_STATUS_CONTEXT +Get or set any parameters of type +.Va void * +that are needed for the status callback function. +.El +.Pp +The +.Va removefile_callback_t +function pointer is defined as the following: +.Pp +.Ft int +.Va (*removefile_callback_t) (removefile_state_t state, const char *path , void *context) +.Pp +The return value of the callback function is given as: +.Bl -tag -width REMOVEFILE_PROCEED +.It Dv REMOVEFILE_PROCEED +File is deleted and +.Fn removefile +continues operation as normal. +.It Dv REMOVEFILE_SKIP +Current file is not deleted and +.Fn removefile +continues operation as normal. +.It Dv REMOVEFILE_STOP +Current file is not deleted and +.Fn removefile +exits without continuing further. +.El +.Pp +The +.Fn removefile_cancel +function is used to cancel a remove that is in progress. +.Sh RETURN VALUES +The family of +.Fn removefile +functions returns less than 0 on error, and 0 on success. +.Sh ERRORS +.Fn removefile +will fail if: +.Bl -tag -width Er +.It Bq Er EACCES +The +.Va path +location specifies a file or directory for which the calling process does not +have proper permissions. +.It Bq Er EINVAL +A callback returned an invalid return value (not REMOVEFILE_PROCEED, REMOVEFILE_SKIP, or REMOVEFILE_STOP) +.It Bq Er EMLINK +The +.Va path +location refers to a symbolic link. +.It Bq Er ENAMETOOLONG +The resource fork name of the file exceeds the maximum allowed length. +.It Bq Er ENOMEM +A memory allocation failed. +.It Bq Er ENOTEMPTY +The +.Va path +location specifies a directory that contains an immutable file which cannot be +deleted. +.It Bq Er EPERM +The +.Va path +location specifies an immutable file that cannot be deleted. +.El +.Pp +.Fn removefile_cancel +will fail if: +.Bl -tag -width Er +.It Bq Er EINVAL +A +.Va NULL +parameter was passed into +.Fn removefile_cancel . +.El +.Pp +In addition, all functions may return an error from an underlying library or +system call. +.Sh NOTES +.Pp +Write protected files owned by another user cannot be removed by +.Fn removefile , +regardless of the permissions on the directory containing the file. +.Pp +If multiple of the REMOVEFILE_SECURE_1_PASS, REMOVEFILE_SECURE_7_PASS, and REMOVEFILE_SECURE_35_PASS +flags are specified, +.Fn removefile +will proceed using the flag that specifies the highest number of overwriting passes. +.Pp +.Fn removefile +is pathname-based; this means that, when descending into a hierarchy, there are potential race conditions +that may add risk when run with privileges. +.Pp +.Fn removefile +operates on symbolic links, rather than the target of the link. +.Sh EXAMPLES +.Bd -literal -offset indent +/* Initialize a state variable */ +removefile_state_t s; +s = removefile_state_alloc(); +/* Recursively remove all files and directories while keeping parent tmp directory. */ +removefile("/tmp", s, REMOVEFILE_RECURSIVE | REMOVEFILE_KEEP_PARENT); +/* Release the state variable */ +removefile_state_free(s); +.Pp +/* A more complex way to call removefile() -- define a callback function */ +int removefile_status_callback(removefile_state_t state, const char * path, void * context) { + fprintf(stderr, "File deleted: %s", path); + return REMOVEFILE_PROCEED; +} +/* Initialize a state variable */ +s = removefile_state_alloc(); +/* Set callback function properties */ +removefile_state_set(s, REMOVEFILE_STATE_CONFIRM_CALLBACK, removefile_confirm_callback); +removefile_state_set(s, REMOVEFILE_STATE_CONFIRM_CONTEXT, NULL); +/* Recursively remove all files and directories while keeping parent tmp directory, + calling a confirm callback prior to each file deletion. */ +removefile("/tmp", s, REMOVEFILE_RECURSIVE | REMOVEFILE_KEEP_PARENT); +/* Release the state variable. */ +removefile_state_free(s); +.Ed +.Sh "SEE ALSO" +.Xr srm 1 , +.Xr unlink 1 , +.Xr sync 2 , +.Xr sync_volume_np 3 +.Sh HISTORY +The +.Fn removefile +API was introduced in Mac OS X 10.5. diff --git a/src/libremovefile/removefile.c b/src/libremovefile/removefile.c new file mode 100644 index 000000000..0b5196876 --- /dev/null +++ b/src/libremovefile/removefile.c @@ -0,0 +1,139 @@ +#include "removefile.h" +#include "removefile_priv.h" + +#include + +removefile_state_t +removefile_state_alloc() { + removefile_state_t state = (removefile_state_t)calloc(1, sizeof(struct _removefile_state)); + if (state != NULL) + state->urand_file = -1; + return state; +} + +int +removefile_state_free(removefile_state_t state) { + if (state != NULL) { + if (state->urand_file != -1) { + close(state->urand_file); + state->urand_file = -1; + } + if (state->buffer != NULL) { + free(state->buffer); + state->buffer = NULL; + } + free(state); + } + return 0; +} + +int +removefile_state_get(removefile_state_t state, uint32_t key, void* dst) { + switch(key) { + case REMOVEFILE_STATE_CONFIRM_CALLBACK: + *(removefile_callback_t*)dst = state->confirm_callback; + break; + case REMOVEFILE_STATE_CONFIRM_CONTEXT: + *(void**)dst = state->confirm_context; + break; + case REMOVEFILE_STATE_ERROR_CALLBACK: + *(removefile_callback_t*)dst = state->error_callback; + break; + case REMOVEFILE_STATE_ERROR_CONTEXT: + *(void**)dst = state->error_context; + break; + case REMOVEFILE_STATE_ERRNO: + *(int*)dst = state->error_num; + break; + case REMOVEFILE_STATE_STATUS_CALLBACK: + *(removefile_callback_t*)dst = state->status_callback; + break; + case REMOVEFILE_STATE_STATUS_CONTEXT: + *(void**)dst = state->status_context; + break; + default: + errno = EINVAL; + return -1; + } + return 0; +} + +int +removefile_state_set(removefile_state_t state, uint32_t key, const void* value) { + switch(key) { + case REMOVEFILE_STATE_CONFIRM_CALLBACK: + state->confirm_callback = value; + break; + case REMOVEFILE_STATE_CONFIRM_CONTEXT: + state->confirm_context = (void *) value; + break; + case REMOVEFILE_STATE_ERROR_CALLBACK: + state->error_callback = value; + break; + case REMOVEFILE_STATE_ERROR_CONTEXT: + state->error_context = (void *) value; + break; + case REMOVEFILE_STATE_ERRNO: + state->error_num = *(int*)value; + break; + case REMOVEFILE_STATE_STATUS_CALLBACK: + state->status_callback = value; + break; + case REMOVEFILE_STATE_STATUS_CONTEXT: + state->status_context = (void *) value; + break; + default: + errno = EINVAL; + return -1; + } + return 0; +} + +int +removefile(const char* path, removefile_state_t state_param, removefile_flags_t flags) { + int res = 0; + char* paths[] = { NULL, NULL }; + removefile_state_t state = state_param; + + // allocate the state if it was not passed in + if (state_param == NULL) { + state = removefile_state_alloc(); + if (state == NULL) { + errno = ENOMEM; + return -1; + } + } + state->cancelled = 0; + state->unlink_flags = flags; + + if (flags & (REMOVEFILE_SECURE_7_PASS | REMOVEFILE_SECURE_35_PASS | REMOVEFILE_SECURE_1_PASS | REMOVEFILE_SECURE_3_PASS)) { + __removefile_init_random(getpid(), state); + } + + paths[0] = strdup(path); + if (paths[0]) { + res = __removefile_tree_walker(paths, state); + free(paths[0]); + } else { + errno = ENOMEM; + res = -1; + } + + // deallocate if allocated locally + if (state_param == NULL) { + removefile_state_free(state); + } + + return res; +} + +int +removefile_cancel(removefile_state_t state) { + if (state == NULL) { + errno = EINVAL; + return -1; + } else { + state->cancelled = 1; + return 0; + } +} diff --git a/src/libremovefile/removefile.h b/src/libremovefile/removefile.h new file mode 100644 index 000000000..6197ae07b --- /dev/null +++ b/src/libremovefile/removefile.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + */ + +#ifndef __REMOVEFILE_H__ +#define __REMOVEFILE_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Flags + */ +typedef uint32_t removefile_flags_t; + +enum { + REMOVEFILE_RECURSIVE = (1 << 0), // If path is a directory, recurse (depth first traversal) + REMOVEFILE_KEEP_PARENT = (1 << 1), // Remove contents but not directory itself + REMOVEFILE_SECURE_7_PASS = (1 << 2), // 7 pass DoD algorithm + REMOVEFILE_SECURE_35_PASS = (1 << 3), // 35-pass Gutmann algorithm (overrides REMOVEFILE_SECURE_7_PASS) + REMOVEFILE_SECURE_1_PASS = (1 << 4), // 1 pass single overwrite + REMOVEFILE_SECURE_3_PASS = (1 << 5), // 3 pass overwrite + REMOVEFILE_SECURE_1_PASS_ZERO = (1 << 6), // Single-pass overwrite, with 0 instead of random data +}; + +/* + * State object to pass in callback information + */ +typedef struct _removefile_state * removefile_state_t; + +removefile_state_t removefile_state_alloc(void); +int removefile_state_free(removefile_state_t); + +int removefile_state_get(removefile_state_t state, uint32_t key, void * dst); +int removefile_state_set(removefile_state_t state, uint32_t key, const void* value); + +enum { + REMOVEFILE_STATE_CONFIRM_CALLBACK = 1, // removefile_callback_t + REMOVEFILE_STATE_CONFIRM_CONTEXT = 2, // void* + REMOVEFILE_STATE_ERROR_CALLBACK = 3, // removefile_callback_t + REMOVEFILE_STATE_ERROR_CONTEXT = 4, // void* + REMOVEFILE_STATE_ERRNO = 5, // int (read-only) + REMOVEFILE_STATE_STATUS_CALLBACK = 6, // removefile_callback_t + REMOVEFILE_STATE_STATUS_CONTEXT = 7, // void* +}; + +typedef int (*removefile_callback_t)(removefile_state_t state, const char* path, void* context); + +/* + * Callback return values + */ +enum { + REMOVEFILE_PROCEED = 0, + REMOVEFILE_SKIP = 1, + REMOVEFILE_STOP = 2, +}; + + +int removefile(const char* path, removefile_state_t state, removefile_flags_t flags); + +int removefile_cancel(removefile_state_t state); + +#ifdef __cplusplus +} +#endif + +#endif /* __REMOVEFILE_H__ */ diff --git a/src/libremovefile/removefile.plist b/src/libremovefile/removefile.plist new file mode 100644 index 000000000..783487ae3 --- /dev/null +++ b/src/libremovefile/removefile.plist @@ -0,0 +1,22 @@ + + + + + OpenSourceImportDate + 2004-09-22 + OpenSourceLicense + MIT + OpenSourceLicenseFile + removefile.txt + OpenSourceProject + srm + OpenSourceSHA1 + 8b7aafc95b3ef67e2c737030567222bf50082188 + OpenSourceURL + http://prdownloads.sourceforge.net/srm/srm-1.2.8.tar.gz?download + OpenSourceVersion + 1.2.8 + OpenSourceWebsiteURL + http://sourceforge.net/projects/srm/ + + diff --git a/src/libremovefile/removefile.xcodeproj/project.pbxproj b/src/libremovefile/removefile.xcodeproj/project.pbxproj new file mode 100644 index 000000000..31a0c6ecf --- /dev/null +++ b/src/libremovefile/removefile.xcodeproj/project.pbxproj @@ -0,0 +1,280 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + FCCE179913592780002CEE6D /* checkint.h in Headers */ = {isa = PBXBuildFile; fileRef = FCCE179013592780002CEE6D /* checkint.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FCCE179A13592780002CEE6D /* removefile_priv.h in Headers */ = {isa = PBXBuildFile; fileRef = FCCE179113592780002CEE6D /* removefile_priv.h */; settings = {ATTRIBUTES = (); }; }; + FCCE179B13592780002CEE6D /* removefile_random.c in Sources */ = {isa = PBXBuildFile; fileRef = FCCE179213592780002CEE6D /* removefile_random.c */; }; + FCCE179C13592780002CEE6D /* removefile_rename_unlink.c in Sources */ = {isa = PBXBuildFile; fileRef = FCCE179313592780002CEE6D /* removefile_rename_unlink.c */; }; + FCCE179D13592780002CEE6D /* removefile_sunlink.c in Sources */ = {isa = PBXBuildFile; fileRef = FCCE179413592780002CEE6D /* removefile_sunlink.c */; }; + FCCE179E13592780002CEE6D /* removefile_tree_walker.c in Sources */ = {isa = PBXBuildFile; fileRef = FCCE179513592780002CEE6D /* removefile_tree_walker.c */; }; + FCCE179F13592780002CEE6D /* removefile.c in Sources */ = {isa = PBXBuildFile; fileRef = FCCE179713592780002CEE6D /* removefile.c */; }; + FCCE17A013592780002CEE6D /* removefile.h in Headers */ = {isa = PBXBuildFile; fileRef = FCCE179813592780002CEE6D /* removefile.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FCCE17DE135A69CD002CEE6D /* test-removefile.c in Sources */ = {isa = PBXBuildFile; fileRef = FCCE17DD135A69CD002CEE6D /* test-removefile.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 3FE582A1185C51BD007B3FA6 /* removefile.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = removefile.xcconfig; path = xcodescripts/removefile.xcconfig; sourceTree = ""; }; + FCCE1789135924FC002CEE6D /* libremovefile.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libremovefile.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + FCCE178F13592780002CEE6D /* checkint.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = checkint.3; sourceTree = ""; }; + FCCE179013592780002CEE6D /* checkint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = checkint.h; sourceTree = ""; }; + FCCE179113592780002CEE6D /* removefile_priv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = removefile_priv.h; sourceTree = ""; }; + FCCE179213592780002CEE6D /* removefile_random.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = removefile_random.c; sourceTree = ""; }; + FCCE179313592780002CEE6D /* removefile_rename_unlink.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = removefile_rename_unlink.c; sourceTree = ""; }; + FCCE179413592780002CEE6D /* removefile_sunlink.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = removefile_sunlink.c; sourceTree = ""; }; + FCCE179513592780002CEE6D /* removefile_tree_walker.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = removefile_tree_walker.c; sourceTree = ""; }; + FCCE179613592780002CEE6D /* removefile.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = removefile.3; sourceTree = ""; }; + FCCE179713592780002CEE6D /* removefile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = removefile.c; sourceTree = ""; }; + FCCE179813592780002CEE6D /* removefile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = removefile.h; sourceTree = ""; }; + FCCE17A5135A3E9C002CEE6D /* install_files.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = install_files.sh; path = xcodescripts/install_files.sh; sourceTree = ""; }; + FCCE17D3135A6961002CEE6D /* test-removefile */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "test-removefile"; sourceTree = BUILT_PRODUCTS_DIR; }; + FCCE17DD135A69CD002CEE6D /* test-removefile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "test-removefile.c"; path = "test/test-removefile.c"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + FCCE1786135924FC002CEE6D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCCE17D0135A6961002CEE6D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + FCCE1779135924CD002CEE6D = { + isa = PBXGroup; + children = ( + 3FE582A1185C51BD007B3FA6 /* removefile.xcconfig */, + FCCE179813592780002CEE6D /* removefile.h */, + FCCE179113592780002CEE6D /* removefile_priv.h */, + FCCE179613592780002CEE6D /* removefile.3 */, + FCCE179713592780002CEE6D /* removefile.c */, + FCCE179213592780002CEE6D /* removefile_random.c */, + FCCE179313592780002CEE6D /* removefile_rename_unlink.c */, + FCCE179413592780002CEE6D /* removefile_sunlink.c */, + FCCE179513592780002CEE6D /* removefile_tree_walker.c */, + FCCE17DD135A69CD002CEE6D /* test-removefile.c */, + FCCE17A5135A3E9C002CEE6D /* install_files.sh */, + FCCE179013592780002CEE6D /* checkint.h */, + FCCE178F13592780002CEE6D /* checkint.3 */, + FCCE178A135924FC002CEE6D /* Products */, + ); + sourceTree = ""; + }; + FCCE178A135924FC002CEE6D /* Products */ = { + isa = PBXGroup; + children = ( + FCCE1789135924FC002CEE6D /* libremovefile.dylib */, + FCCE17D3135A6961002CEE6D /* test-removefile */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + FCCE1787135924FC002CEE6D /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + FCCE179913592780002CEE6D /* checkint.h in Headers */, + FCCE17A013592780002CEE6D /* removefile.h in Headers */, + FCCE179A13592780002CEE6D /* removefile_priv.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + FCCE1788135924FC002CEE6D /* removefile */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCCE178D135924FD002CEE6D /* Build configuration list for PBXNativeTarget "removefile" */; + buildPhases = ( + FCCE1785135924FC002CEE6D /* Sources */, + FCCE1786135924FC002CEE6D /* Frameworks */, + FCCE1787135924FC002CEE6D /* Headers */, + FCCE17A4135A3E89002CEE6D /* Run Script */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = removefile; + productName = removefile; + productReference = FCCE1789135924FC002CEE6D /* libremovefile.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; + FCCE17D2135A6961002CEE6D /* test-removefile */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCCE17DB135A6961002CEE6D /* Build configuration list for PBXNativeTarget "test-removefile" */; + buildPhases = ( + FCCE17CF135A6961002CEE6D /* Sources */, + FCCE17D0135A6961002CEE6D /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "test-removefile"; + productName = "test-removefile"; + productReference = FCCE17D3135A6961002CEE6D /* test-removefile */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + FCCE177B135924CD002CEE6D /* Project object */ = { + isa = PBXProject; + attributes = { + ORGANIZATIONNAME = "Apple Inc."; + }; + buildConfigurationList = FCCE177E135924CD002CEE6D /* Build configuration list for PBXProject "removefile" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = FCCE1779135924CD002CEE6D; + productRefGroup = FCCE178A135924FC002CEE6D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + FCCE1788135924FC002CEE6D /* removefile */, + FCCE17D2135A6961002CEE6D /* test-removefile */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXShellScriptBuildPhase section */ + FCCE17A4135A3E89002CEE6D /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = ". \"$PROJECT_DIR\"/xcodescripts/install_files.sh"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + FCCE1785135924FC002CEE6D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCCE179B13592780002CEE6D /* removefile_random.c in Sources */, + FCCE179C13592780002CEE6D /* removefile_rename_unlink.c in Sources */, + FCCE179D13592780002CEE6D /* removefile_sunlink.c in Sources */, + FCCE179E13592780002CEE6D /* removefile_tree_walker.c in Sources */, + FCCE179F13592780002CEE6D /* removefile.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCCE17CF135A6961002CEE6D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCCE17DE135A69CD002CEE6D /* test-removefile.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + FCCE1781135924CD002CEE6D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Release; + }; + FCCE178C135924FD002CEE6D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3FE582A1185C51BD007B3FA6 /* removefile.xcconfig */; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = "$(RC_ProjectSourceVersion)"; + EXECUTABLE_PREFIX = lib; + GCC_PREPROCESSOR_DEFINITIONS = "__DARWIN_NON_CANCELABLE=1"; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + LINK_WITH_STANDARD_LIBRARIES = NO; + OTHER_LDFLAGS = ( + "-Wl,-umbrella,System", + "-L/usr/lib/system", + "-ldyld", + "-lcompiler_rt", + "-lsystem_kernel", + "-lsystem_platform", + "-lsystem_malloc", + "-lsystem_c", + ); + "OTHER_LDFLAGS[sdk=iphonesimulator*]" = ( + "-Wl,-umbrella,System", + "-L/usr/lib/system", + "-ldyld", + "-lcompiler_rt", + "-lsystem_sim_kernel", + "-lsystem_sim_platform", + "-lsystem_malloc", + "-lsystem_sim_c", + ); + WARNING_CFLAGS = "-Wall"; + }; + name = Release; + }; + FCCE17DA135A6961002CEE6D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + FCCE177E135924CD002CEE6D /* Build configuration list for PBXProject "removefile" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCCE1781135924CD002CEE6D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCCE178D135924FD002CEE6D /* Build configuration list for PBXNativeTarget "removefile" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCCE178C135924FD002CEE6D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCCE17DB135A6961002CEE6D /* Build configuration list for PBXNativeTarget "test-removefile" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCCE17DA135A6961002CEE6D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = FCCE177B135924CD002CEE6D /* Project object */; +} diff --git a/src/libremovefile/removefile_priv.h b/src/libremovefile/removefile_priv.h new file mode 100644 index 000000000..7b670026f --- /dev/null +++ b/src/libremovefile/removefile_priv.h @@ -0,0 +1,62 @@ +#ifdef __cplusplus +extern "C" { +#endif + +#include "removefile.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This structure contains variables necessary to keep track of verification, + * the flags set by the user, as well as associated buffers and file + * attributes. These were defined previously by srm as global variables but a + * structure is a much cleaner way of organizing the information and passing + * it between functions. + */ + +struct _removefile_state { + removefile_callback_t confirm_callback; + void * confirm_context; + removefile_callback_t error_callback; + void * error_context; + int error_num; // clear on proceed + removefile_callback_t status_callback; + void * status_context; + + // globals for srm + int urand_file; + off_t random_bytes_read; + int file; + off_t file_size; + unsigned char * buffer; + uint32_t buffsize; + uint32_t allocated_buffsize; + int unlink_flags; + int cancelled; +}; + +int __removefile_rename_unlink(const char*path, removefile_state_t state); +int __removefile_tree_walker(char ** trees, removefile_state_t state); +int __removefile_sunlink(const char * path, removefile_state_t state); +void __removefile_init_random(const unsigned int seed, removefile_state_t state); +char __removefile_random_char(removefile_state_t state); +void __removefile_randomize_buffer(unsigned char *buffer, size_t length, removefile_state_t state); + +#define __removefile_state_test_cancel(s) ((s)->cancelled != 0) + +#ifdef __cplusplus +} +#endif + diff --git a/src/libremovefile/removefile_random.c b/src/libremovefile/removefile_random.c new file mode 100644 index 000000000..7360b3669 --- /dev/null +++ b/src/libremovefile/removefile_random.c @@ -0,0 +1,119 @@ +/* srm */ +/* Copyright (c) 2000 Matthew D. Gauthier + * Portions copyright (c) 2007 Apple Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of the contributors shall + * not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization. + */ + +#include +#include +#include +#include +#include +#include + +#include "removefile.h" +#include "removefile_priv.h" + +static void +seed_random(removefile_state_t state) { +#ifdef USE_ARC4RANDOM + arc4random_stir(); +#else + unsigned int rseed; + struct timeval tv; + struct timezone tz; + + if (state->urand_file != -1) { + read(state->urand_file, &rseed, sizeof(rseed)); + } else { + rseed = rand(); + } + (void)gettimeofday(&tv, &tz); + rseed ^= tv.tv_sec + tv.tv_usec + getpid(); + srand(rseed); +#endif +} + +void +__removefile_init_random(const unsigned int seed, removefile_state_t state) { +#ifdef USE_ARC4RANDOM + arc4random_addrandom((unsigned char *)&seed, sizeof(seed)); +#else + struct stat statbuf; + + if (stat("/dev/urandom", &statbuf) == 0 && S_ISCHR(statbuf.st_mode)) { + state->urand_file = open("/dev/urandom", O_RDONLY); + } else { + srand(seed); + } + seed_random(state); +#endif +} + +char +__removefile_random_char(removefile_state_t state) { +#ifdef USE_ARC4RANDOM + random_bytes_read += 4; + return arc4random(); +#else + char buf[4]; + + if (state->urand_file != -1) { + read(state->urand_file, &buf, 1); + return buf[0]; + } + return rand(); +#endif +} + +void +__removefile_randomize_buffer(unsigned char *buffer, size_t length, removefile_state_t state) { + size_t i; + +#ifdef USE_ARC4RANDOM + u_int32_t *p = (u_int32_t *)buffer; + u_int32_t mod4length = length - (length % 4); + + for (i = 0; i < mod4length; i += 4) { + *p++ = arc4random(); + } + + while (i < length) { + buffer[i++] = arc4random(); + } + state->random_bytes_read += (mod4length + ((length - mod4length) * 4)); + if (state->random_bytes_read > 512*1024*1024 /* RESEED_BYTES */) { + state->random_bytes_read = 0; + seed_random(state); + } +#else + if (state->urand_file != -1) { + read(state->urand_file, buffer, length); + } else { + for (i = 0; i < length; i++) + buffer[i] = rand(); + } +#endif +} diff --git a/src/libremovefile/removefile_rename_unlink.c b/src/libremovefile/removefile_rename_unlink.c new file mode 100644 index 000000000..b2522e545 --- /dev/null +++ b/src/libremovefile/removefile_rename_unlink.c @@ -0,0 +1,115 @@ +/* srm */ +/* Copyright (c) 2000 Matthew D. Gauthier + * Portions copyright (c) 2007 Apple Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of the contributors shall + * not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "removefile.h" +#include "removefile_priv.h" + +static int empty_directory(const char *path) { + DIR *dp; + struct dirent *de; + + dp = opendir(path); + if (dp == NULL) { + return -1; + } + while ((de = readdir(dp)) != NULL) { + if (de->d_namlen < 3 && (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))) { + continue; + } + (void)closedir(dp); + return -1; + } + (void)closedir(dp); + return 0; +} + +int +__removefile_rename_unlink(const char *path, removefile_state_t state) { + char *p, c; + struct stat statbuf; + + size_t new_name_size = strlen(path) + 15; + char new_name[new_name_size]; + int i = 0; + + strlcpy(new_name, path, new_name_size); + + if ( (p = strrchr(new_name, '/')) != NULL ) { + p++; + *p = '\0'; + } else { + p = new_name; + } + + do { + i = 0; + + while (i < 14) { + c = __removefile_random_char(state); + if (isalnum((int) c)) { + p[i] = c; + i++; + } + } + p[i] = '\0'; + } while (lstat(new_name, &statbuf) == 0); + + if (lstat(path, &statbuf) == -1) + return -1; + + if (S_ISDIR(statbuf.st_mode) && (empty_directory(path) == -1)) { + /* Directory isn't empty (e.g. because it contains an immutable file). + Attempting to remove it will fail, so avoid renaming it. */ + errno = ENOTEMPTY; + return -1; + } + + if (rename(path, new_name) == -1) + return -1; + + if (lstat(new_name, &statbuf) == -1) { + errno = ENOENT; + return -1; + } + + if (S_ISDIR(statbuf.st_mode)) + return rmdir(new_name); + + return unlink(new_name); +} diff --git a/src/libremovefile/removefile_sunlink.c b/src/libremovefile/removefile_sunlink.c new file mode 100644 index 000000000..fd6607120 --- /dev/null +++ b/src/libremovefile/removefile_sunlink.c @@ -0,0 +1,456 @@ +/* srm */ +/* Copyright (c) 2000 Matthew D. Gauthier + * Portions copyright (c) 2007 Apple Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of the contributors shall + * not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_LINUX_EXT2_FS_H +#include +#endif + +#if HAVE_SYS_VFS_H +#include +#endif + +#if HAVE_SYS_PARAM_H && HAVE_SYS_MOUNT_H +#include +#include +#endif + +#if __APPLE__ +#include +#endif + +#if HAVE_CHFLAGS +/* define unsupported flags as 0 */ +# if !defined UF_IMMUTABLE +# define UF_IMMUTABLE 0 +# endif +# if !defined UF_APPEND +# define UF_APPEND 0 +# endif +# if !defined UF_NOUNLINK +# define UF_NOUNLINK 0 +# endif +# if !defined SF_IMMUTABLE +# define SF_IMMUTABLE 0 +# endif +# if !defined SF_APPEND +# define SF_APPEND 0 +# endif +# if !defined SF_NOUNLINK +# define SF_NOUNLINK 0 +# endif +#endif + +#include "removefile.h" +#include "removefile_priv.h" + +static int +init_write_buffer(struct stat *statbuf, struct statfs *fs_stats, removefile_state_t state) { + u_int32_t tmp_buffsize; + + state->file_size = statbuf->st_size; + state->buffsize = statbuf->st_blksize; + +#if HAVE_SYS_PARAM_H + /* try to determine an optimal write buffer size */ + state->buffsize = (u_int32_t)(statbuf->st_size / statbuf->st_blksize) * statbuf->st_blksize; + if ((statbuf->st_size % statbuf->st_blksize) != 0) { + /* add full size of last block */ + state->buffsize += statbuf->st_blksize; + } else if (state->buffsize < statbuf->st_blksize) { + /* no smaller than one device block */ + state->buffsize = statbuf->st_blksize; + } + tmp_buffsize = MAXBSIZE; + if (state->buffsize > tmp_buffsize) { + /* no larger than the largest file system buffer size */ + state->buffsize = tmp_buffsize; + } +#endif + + /* Allocated buffer must be at least 2 bytes larger than logical buffsize. + This lets us align repeating 3-byte patterns across multiple buffer + writes by using a variable offset (0..2) from the start of the buffer. */ + + tmp_buffsize = state->buffsize + 4; + + if (state->buffer) { + if (tmp_buffsize > state->allocated_buffsize) { + free(state->buffer); + state->buffer = NULL; + } else { + return 0; /* use existing buffer */ + } + } + if ((state->buffer = (unsigned char *)malloc(tmp_buffsize)) == NULL) { + errno = ENOMEM; + return -1; + } + state->allocated_buffsize = tmp_buffsize; + return 0; +} + +static void +flush(int fd) { + /* force buffered writes to be flushed to disk */ +#if defined F_FULLFSYNC + /* F_FULLFSYNC is equivalent to fsync plus device flush to media */ + if (fcntl(fd, F_FULLFSYNC, NULL) != 0) { + /* we're not on a fs that supports this; fall back to plain fsync */ + fsync(fd); + } +#elif HAVE_FDATASYNC + fdatasync(fd); +#else + fsync(fd); +#endif +} + +static unsigned char *align_buffer(unsigned char *buf, off_t pos) { + /* return a pointer to the start of the buffer which should be written, + offset from the given buffer by 0, 1, or 2 bytes, so that the 3-byte + pattern which the buffer contains is aligned with the previous write. */ + return (unsigned char *)((uintptr_t)buf + (unsigned int)(pos % 3)); +} + +#if 0 /* UNUSED */ +void verification_failure(off_t count) { + if (sizeof(off_t) == 4) + printf("warning: failed to verify write at offset %d\n", count); + else if (sizeof(off_t) == 8) + printf("warning: failed to verify write at offset %lld\n", count); + else + printf("warning: previous write failed to verify!\n"); + fflush(stdout); +} +#endif /* UNUSED */ + +static void +overwrite(int stage, removefile_state_t state) { + ssize_t i; + off_t count = 0; + unsigned char *buffptr = state->buffer; + + // break out of the function early if cancel is detected + if (__removefile_state_test_cancel(state)) return; + + lseek(state->file, 0, SEEK_SET); + while (count < state->file_size - state->buffsize) { + if (stage == 1 /* W_RANDOM */) { + __removefile_randomize_buffer(state->buffer, state->buffsize, state); + } else if (stage == 2 /* W_TRIPLE */) { + buffptr = align_buffer(state->buffer, count); + } + i = write(state->file, buffptr, state->buffsize); + if (i > 0) + count += i; + + // break out of the loop early if cancel is detected + if (__removefile_state_test_cancel(state)) return; + } + if (stage == 1 /* W_RANDOM */) { + __removefile_randomize_buffer(state->buffer, (size_t)(state->file_size - count), state); + } else if (stage == 2 /* W_TRIPLE */) { + buffptr = align_buffer(state->buffer, count); + } + i = write(state->file, buffptr, (size_t)(state->file_size - count)); + /* + * Only flush the data if we're doing more than one pass of writes. + */ + if ((state->unlink_flags & (REMOVEFILE_SECURE_7_PASS | REMOVEFILE_SECURE_35_PASS | REMOVEFILE_SECURE_3_PASS)) != 0) + flush(state->file); + lseek(state->file, 0, SEEK_SET); +} + +static void +overwrite_random(int num_passes, removefile_state_t state) { + int i; + + for (i = 0; i < num_passes; i++) { + overwrite(1 /* W_RANDOM */, state); + } +} + +static void +overwrite_byte(int byte, removefile_state_t state) { + memset(state->buffer, byte, state->buffsize); + overwrite(0 /* W_SINGLE */, state); +} + +static void +overwrite_bytes(unsigned int byte1, unsigned int byte2, unsigned int byte3, removefile_state_t state) { + u_int32_t val[3], *p = (u_int32_t *)state->buffer; + unsigned int i, mod12buffsize = state->allocated_buffsize - (state->allocated_buffsize % 12); + + val[0] = (byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte1; + val[1] = (byte2 << 24) | (byte3 << 16) | (byte1 << 8) | byte2; + val[2] = (byte3 << 24) | (byte1 << 16) | (byte2 << 8) | byte3; + + /* fill buffer 12 bytes at a time, optimized for 4-byte alignment */ + for (i = 0; i < mod12buffsize; i += 12) { + *p++ = val[0]; + *p++ = val[1]; + *p++ = val[2]; + } + while (i < state->allocated_buffsize) { + state->buffer[i] = ((unsigned char *)&val[0])[i % 3]; + i++; + } + overwrite(2 /* W_TRIPLE */, state); +} + +static void +overwrite_file(removefile_state_t state) { + if (state->unlink_flags & REMOVEFILE_SECURE_35_PASS) { + /* Gutmann 35-pass overwrite */ + overwrite_random(4, state); + overwrite_byte(0x55, state); + overwrite_byte(0xAA, state); + overwrite_bytes(0x92, 0x49, 0x24, state); + overwrite_bytes(0x49, 0x24, 0x92, state); + overwrite_bytes(0x24, 0x92, 0x49, state); + overwrite_byte(0x00, state); + overwrite_byte(0x11, state); + overwrite_byte(0x22, state); + overwrite_byte(0x33, state); + overwrite_byte(0x44, state); + overwrite_byte(0x55, state); + overwrite_byte(0x66, state); + overwrite_byte(0x77, state); + overwrite_byte(0x88, state); + overwrite_byte(0x99, state); + overwrite_byte(0xAA, state); + overwrite_byte(0xBB, state); + overwrite_byte(0xCC, state); + overwrite_byte(0xDD, state); + overwrite_byte(0xEE, state); + overwrite_byte(0xFF, state); + overwrite_bytes(0x92, 0x49, 0x24, state); + overwrite_bytes(0x49, 0x24, 0x92, state); + overwrite_bytes(0x24, 0x92, 0x49, state); + overwrite_bytes(0x6D, 0xB6, 0xDB, state); + overwrite_bytes(0xB6, 0xDB, 0x6D, state); + overwrite_bytes(0xDB, 0x6D, 0xB6, state); + overwrite_random(4, state); + } else if (state->unlink_flags & REMOVEFILE_SECURE_7_PASS) { + /* DoD-compliant 7-pass overwrite */ + overwrite_byte(0xF6, state); + overwrite_byte(0x00, state); + overwrite_byte(0xFF, state); + overwrite_random(1, state); + overwrite_byte(0x00, state); + overwrite_byte(0xFF, state); + overwrite_random(1, state); + } else if (state->unlink_flags & REMOVEFILE_SECURE_3_PASS) { + /* DOE M2051-2 or DOD 5220.22-M */ + overwrite_random(2, state); + overwrite_byte(0xAA, state); + } else if (state->unlink_flags & REMOVEFILE_SECURE_1_PASS) { + overwrite_random(1, state); + } else if (state->unlink_flags & REMOVEFILE_SECURE_1_PASS_ZERO) { + overwrite_byte(0, state); + } +} + +int +__removefile_sunlink(const char *path, removefile_state_t state) { + struct stat statbuf; + struct statfs fs_stats; +#if HAVE_LINUX_EXT2_FS_H + int flags = 0; +#endif + int fmode = O_WRONLY; + struct flock flock; + + if (lstat(path, &statbuf) == -1) + return -1; + if (!S_ISREG(statbuf.st_mode)) + return __removefile_rename_unlink(path, state); + if (statbuf.st_nlink > 1) { + return __removefile_rename_unlink(path, state); + } + + if ( (state->file = open(path, fmode)) == -1) /* BSD doesn't support O_SYNC */ + return -1; + if (fcntl(state->file, F_WRLCK, &flock) == -1) { + close(state->file); + state->file = -1; + return -1; + } + + if (fstatfs(state->file, &fs_stats) == -1 && errno != ENOSYS) { + close(state->file); + state->file = -1; + return -1; + } + +#if HAVE_LINUX_EXT2_FS_H + if (fs_stats.f_type == EXT2_SUPER_MAGIC) + if (ioctl(state->file, EXT2_IOC_GETFLAGS, &flags) == -1) { + close(state->file); + state->file = -1; + return -1; + } + + if ( (flags & EXT2_UNRM_FL) || (flags & EXT2_IMMUTABLE_FL) || + (flags & EXT2_APPEND_FL) ) + { + close(state->file); + state->file = -1; + errno = EPERM; + return -1; + } + +#endif /* HAVE_LINUX_EXT2_FS_H */ + +/* chflags(2) turns out to be a different system call in every BSD + derivative. The important thing is to make sure we'll be able to + unlink it after we're through messing around. Unlinking it first + would remove the need for any of these checks, but would leave the + user with no way to overwrite the file if the process was + interrupted during the overwriting. So, instead we assume that the + open() above will fail on immutable and append-only files and try + and catch only platforms supporting NOUNLINK here. + + FreeBSD - supports NOUNLINK (from 4.4 on?) + MacOS X - doesn't support NOUNLINK (as of 10.3.5) + OpenBSD - doesn't support NOUNLINK (as of 3.1) + Tru64 - unknown + + Note: unsupported flags are defined as 0 at the top of this file, + so a specific platform check is not required here. +*/ + +#if HAVE_CHFLAGS + if ((statbuf.st_flags & UF_IMMUTABLE) || + (statbuf.st_flags & UF_APPEND) || + (statbuf.st_flags & UF_NOUNLINK) || + (statbuf.st_flags & SF_IMMUTABLE) || + (statbuf.st_flags & SF_APPEND) || + (statbuf.st_flags & SF_NOUNLINK)) + { + close(state->file); + state->file = -1; + errno = EPERM; + return -1; + } +#endif /* HAVE_CHFLAGS */ + + if (init_write_buffer(&statbuf, &fs_stats, state) == -1) { + close(state->file); + state->file = -1; + return -1; + } +#if defined F_NOCACHE + /* before performing file I/O, set F_NOCACHE to prevent caching */ + (void)fcntl(state->file, F_NOCACHE, 1); +#endif + overwrite_file(state); +#if HAVE_LINUX_EXT2_FS_H + ioctl(state->file, EXT2_IOC_SETFLAGS, EXT2_SECRM_FL); +#endif + + close(state->file); + state->file = -1; +#if __APPLE__ + /* Also overwrite the file's resource fork, if present. */ + { + static const char *RSRCFORKSPEC = "/..namedfork/rsrc"; + off_t rsrc_fork_size; + size_t rsrc_path_size = strlen(path) + strlen(RSRCFORKSPEC) + 1; + char *rsrc_path = (char *)alloca(rsrc_path_size); + if (rsrc_path == NULL) { + errno = ENOMEM; + return -1; + } + if (snprintf(rsrc_path, MAXPATHLEN, + "%s%s", path, RSRCFORKSPEC ) > MAXPATHLEN - 1) { + errno = ENAMETOOLONG; + return -1; + } + + if (lstat(rsrc_path, &statbuf) != 0) { + int err = errno; + if (err == ENOENT || err == ENOTDIR) { + rsrc_fork_size = 0; + } else { + return -1; + } + } else { + rsrc_fork_size = statbuf.st_size; + } + + if (rsrc_fork_size > 0) { + + if ((state->file = open(rsrc_path, O_WRONLY)) == -1) { + return -1; + } + if (fcntl(state->file, F_WRLCK, &flock) == -1) { + close(state->file); + state->file = -1; + return -1; + } + + if (init_write_buffer(&statbuf, &fs_stats, state) == -1) { + close(state->file); + state->file = -1; + return -1; + } + + #if defined F_NOCACHE + /* before performing file I/O, set F_NOCACHE to prevent caching */ + (void)fcntl(state->file, F_NOCACHE, 1); + #endif + + overwrite_file(state); + + close(state->file); + state->file = -1; + } + } +#endif /* __APPLE__ */ + + if (__removefile_state_test_cancel(state)) { + errno = ECANCELED; + return -1; + } + + return __removefile_rename_unlink(path, state); +} diff --git a/src/libremovefile/removefile_tree_walker.c b/src/libremovefile/removefile_tree_walker.c new file mode 100644 index 000000000..d3e2b6b9d --- /dev/null +++ b/src/libremovefile/removefile_tree_walker.c @@ -0,0 +1,218 @@ +/* srm */ +/* Copyright (c) 2000 Matthew D. Gauthier + * Portions copyright (c) 2007 Apple Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of the contributors shall + * not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "removefile.h" +#include "removefile_priv.h" + +int +__removefile_process_file(FTS* stream, FTSENT* current_file, removefile_state_t state) { + int res = 0; + char* path = current_file->fts_path; + + int recursive = state->unlink_flags & REMOVEFILE_RECURSIVE; + int keep_parent = state->unlink_flags & REMOVEFILE_KEEP_PARENT; + int secure = state->unlink_flags & (REMOVEFILE_SECURE_7_PASS | REMOVEFILE_SECURE_35_PASS | REMOVEFILE_SECURE_1_PASS | REMOVEFILE_SECURE_3_PASS | REMOVEFILE_SECURE_1_PASS_ZERO); + + switch (current_file->fts_info) { + // attempt to unlink the directory on pre-order in case it is + // a directory hard-link. If we succeed, it was a hard-link + // and we should not descend any further. + case FTS_D: + if (unlink(path) == 0) { + fts_set(stream, current_file, FTS_SKIP); + } + break; + case FTS_DC: + state->error_num = ELOOP; + res = -1; + break; + case FTS_DNR: + case FTS_ERR: + case FTS_NS: + state->error_num = current_file->fts_errno; + res = -1; + break; + case FTS_DP: + if (recursive && + (!keep_parent || + current_file->fts_level != FTS_ROOTLEVEL)) { + if (secure) { + res = __removefile_rename_unlink(path, + state); + } else { + if (geteuid() == 0 && + (current_file->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && + !(current_file->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && + chflags(path, current_file->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0) { + errno = EACCES; + res = -1; + } else { + res = rmdir(path); + } + } + if (res == -1) state->error_num = errno; + } + break; + case FTS_F: + case FTS_SL: + case FTS_SLNONE: + case FTS_DEFAULT: + if (secure) { + res = __removefile_sunlink(path, state); + } else if (geteuid() == 0 && + (current_file->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && + !(current_file->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && + chflags(path, current_file->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0) { + errno = EACCES; + res = -1; + } else { + res = unlink(path); + } + if (res == -1) state->error_num = errno; + break; + case FTS_DOT: + default: + break; + } + return res; +} + +int +__removefile_tree_walker(char **trees, removefile_state_t state) { + FTSENT *current_file; + FTS *stream; + int rval = 0; + + removefile_callback_t cb_confirm = NULL; + removefile_callback_t cb_status = NULL; + removefile_callback_t cb_error = NULL; + + cb_confirm = state->confirm_callback; + cb_status = state->status_callback; + cb_error = state->error_callback; + + stream = fts_open(trees, FTS_PHYSICAL | FTS_NOCHDIR, NULL); + if (stream == NULL) return -1; + + while ((current_file = fts_read(stream)) != NULL) { + int res = REMOVEFILE_PROCEED; + + /* + * fts_number is set to REMOVEFILE_SKIP for directories where + * the confirmation callback has asked to skip it. We check + * this on post-order, so we don't remove an empty directory that + * the caller wanted preserved. + */ + if (current_file->fts_info == FTS_DP && + current_file->fts_number == REMOVEFILE_SKIP) { + current_file->fts_number = 0; + continue; + } + + // don't process the file if a cancel has been requested + if (__removefile_state_test_cancel(state)) break; + + // confirm regular files and directories in pre-order + if (cb_confirm && current_file->fts_info != FTS_DP) { + res = cb_confirm(state, + current_file->fts_path, state->confirm_context); + } + + // don't process the file if a cancel has been requested + // by the callback + if (__removefile_state_test_cancel(state)) break; + + if (res == REMOVEFILE_PROCEED) { + state->error_num = 0; + rval = __removefile_process_file(stream, current_file, + state); + + if (state->error_num != 0) { + // Since removefile(3) is abstracting the + // traversal of the directory tree, suppress + // all ENOENT and ENOTDIR errors from the + // calling process. + // However, these errors should still be + // reported for the ROOTLEVEL since those paths + // were explicitly provided by the application. + if ((state->error_num != ENOENT && + state->error_num != ENOTDIR) || + current_file->fts_level == FTS_ROOTLEVEL) { + if (cb_error) { + res = cb_error(state, + current_file->fts_path, + state->error_context); + if (res == REMOVEFILE_PROCEED || + res == REMOVEFILE_SKIP) { + rval = 0; + } else if (res == REMOVEFILE_STOP) { + rval = -1; + } + } else { + res = REMOVEFILE_STOP; + } + } + // show status for regular files and directories + // in post-order + } else if (cb_status && + current_file->fts_info != FTS_D) { + res = cb_status(state, current_file->fts_path, + state->status_context); + } + } + + if (current_file->fts_info == FTS_D && res == REMOVEFILE_SKIP) { + current_file->fts_number = REMOVEFILE_SKIP; + } + + if (res == REMOVEFILE_SKIP || + !(state->unlink_flags & REMOVEFILE_RECURSIVE)) + fts_set(stream, current_file, FTS_SKIP); + if (res == REMOVEFILE_STOP || + __removefile_state_test_cancel(state)) + break; + } + + if (__removefile_state_test_cancel(state)) { + errno = ECANCELED; + rval = -1; + } + + fts_close(stream); + return rval; +} diff --git a/src/libremovefile/test/test-removefile.c b/src/libremovefile/test/test-removefile.c new file mode 100644 index 000000000..2d1d2b8c5 --- /dev/null +++ b/src/libremovefile/test/test-removefile.c @@ -0,0 +1,122 @@ +#include "removefile.h" +#include +#include +#include +#include +#include +#include + +static struct timeval tv; +static void start_timer(const char* str) { + fprintf(stderr, "%s... ", str); + assert(gettimeofday(&tv, NULL) == 0); +} + +static void stop_timer() { + struct timeval tv2; + assert(gettimeofday(&tv2, NULL) == 0); + long sec = tv2.tv_sec - tv.tv_sec; + long usec; + if (sec == 0) { + usec = tv2.tv_usec - tv.tv_usec; + } else { + usec = tv2.tv_usec + (1000000 - tv.tv_usec); + } + fprintf(stderr, "%d.%03d\n", sec, usec); +} + + +static int removefile_confirm_callback(removefile_state_t state, const char * path, void * context) { + assert(context == (void*)1234); + fprintf(stderr, "confirm callback: %s\n", path); + return REMOVEFILE_PROCEED; +} + +static int removefile_error_callback(removefile_state_t state, const char * path, void * context) { + assert(context == (void*)4567); + int err = 0; + assert(removefile_state_get(state, REMOVEFILE_STATE_ERRNO, &err) == 0); + fprintf(stderr, "error callback: %s: %s (%d)\n", path, strerror(err), err); + return REMOVEFILE_PROCEED; +} + +static int removefile_status_callback(removefile_state_t state, const char * path, void * context) { + fprintf(stderr, "status callback: %s\n", path); + return REMOVEFILE_PROCEED; +} + + +int mkdirs() { + start_timer("Creating directory structure"); + assert(mkdir("/tmp/removefile-test", 0755) == 0); + assert(mkdir("/tmp/removefile-test/foo", 0755) == 0); + assert(mkdir("/tmp/removefile-test/foo/bar", 0755) == 0); + assert(mkdir("/tmp/removefile-test/foo/baz", 0755) == 0); + int fd; + assert((fd = open("/tmp/removefile-test/foo/baz/woot", O_CREAT | O_TRUNC | O_WRONLY, 0644)) != -1); + write(fd, "Hello World\n", 12); + close(fd); + assert((fd = open("/tmp/removefile-test/foo/baz/wootage", O_CREAT | O_TRUNC | O_WRONLY, 0644)) != -1); + write(fd, "Hello World\n", 12); + assert(lseek(fd, 1024*1024*30, SEEK_SET) != -1); + write(fd, "Goodbye Moon\n", 13); + close(fd); + stop_timer(); +} + +void* threadproc(void* state) { + sleep(1); + fprintf(stderr, "cancelling...\n"); + assert(removefile_cancel(state) == 0); + return NULL; +} + +int main(int argc, char *argv[]) { + removefile_state_t state = NULL; + removefile_callback_t callback = NULL; + pthread_t thread = NULL; + int err = 0; + + mkdirs(); + start_timer("removefile(NULL)"); + assert(removefile("/tmp/removefile-test", NULL, REMOVEFILE_SECURE_1_PASS | REMOVEFILE_RECURSIVE) == 0); + stop_timer(); + + + mkdirs(); + assert((state = removefile_state_alloc()) != NULL); + assert(pthread_create(&thread, NULL, threadproc, state) == 0); + start_timer("removefile(state) with cancel"); + assert(removefile_state_set(state, REMOVEFILE_STATE_ERROR_CALLBACK, removefile_error_callback) == 0); + assert(removefile_state_set(state, REMOVEFILE_STATE_ERROR_CONTEXT, (void*)4567) == 0); + assert(removefile("/tmp/removefile-test", state, REMOVEFILE_SECURE_1_PASS | REMOVEFILE_RECURSIVE) == -1 && errno == ECANCELED); + stop_timer(); + + start_timer("removefile(NULL)"); + assert(removefile("/tmp/removefile-test", NULL, REMOVEFILE_SECURE_1_PASS | REMOVEFILE_RECURSIVE) == 0); + stop_timer(); + + mkdirs(); + assert(removefile_state_set(state, 1234567, (void*)1234567) == -1 && errno == EINVAL); + + assert(removefile_state_set(state, REMOVEFILE_STATE_CONFIRM_CALLBACK, removefile_confirm_callback) == 0); + assert(removefile_state_get(state, REMOVEFILE_STATE_CONFIRM_CALLBACK, &callback) == 0); + assert(callback == removefile_confirm_callback); + assert(removefile_state_set(state, REMOVEFILE_STATE_CONFIRM_CONTEXT, (void*)1234) == 0); + + assert(removefile_state_set(state, REMOVEFILE_STATE_ERROR_CALLBACK, removefile_error_callback) == 0); + assert(removefile_state_get(state, REMOVEFILE_STATE_ERROR_CALLBACK, &callback) == 0); + assert(callback == removefile_error_callback); + assert(removefile_state_set(state, REMOVEFILE_STATE_ERROR_CONTEXT, (void*)4567) == 0); + + assert(removefile_state_set(state, REMOVEFILE_STATE_STATUS_CALLBACK, removefile_status_callback) == 0); + assert(removefile_state_get(state, REMOVEFILE_STATE_STATUS_CALLBACK, &callback) == 0); + assert(callback == removefile_status_callback); + assert(removefile_state_set(state, REMOVEFILE_STATE_STATUS_CONTEXT, (void*)5678) == 0); + + start_timer("removefile(state)"); + assert(removefile("/tmp/removefile-test", state, REMOVEFILE_SECURE_1_PASS | REMOVEFILE_RECURSIVE) == 0); + stop_timer(); + + return 0; +} diff --git a/src/libremovefile/xcodescripts/install_files.sh b/src/libremovefile/xcodescripts/install_files.sh new file mode 100644 index 000000000..50fee6760 --- /dev/null +++ b/src/libremovefile/xcodescripts/install_files.sh @@ -0,0 +1,43 @@ +#!/bin/sh +set -e -x + +# don't install man pages for installhdrs or iOS builds +if [ "$ACTION" = installhdrs ]; then exit 0; fi +if [ "${PLATFORM_NAME/iphone/}" != "${PLATFORM_NAME}" ]; then exit 0; fi + +# open source plist +OSV="$DSTROOT"/usr/local/OpenSourceVersions +OSL="$DSTROOT"/usr/local/OpenSourceLicenses +mkdir -p -m 0755 "$OSV" "$OSL" +install -o "$INSTALL_OWNER" -g "$INSTALL_GROUP" -m 0444 removefile.plist "$OSV" +install -o "$INSTALL_OWNER" -g "$INSTALL_GROUP" -m 0444 LICENSE "$OSL"/removefile.txt + +function InstallManPages() { + for MANPAGE in "$@"; do + SECTION=`basename "${MANPAGE/*./}"` + MANDIR="$DSTROOT"/usr/share/man/man"$SECTION" + install -d -o "$INSTALL_OWNER" -g "$INSTALL_GROUP" -m 0755 "$MANDIR" + install -o "$INSTALL_OWNER" -g "$INSTALL_GROUP" -m 0444 "$MANPAGE" "$MANDIR" + done +} + +function LinkManPages() { + MANPAGE=`basename "$1"` + SECTION=`basename "${MANPAGE/*./}"` + MANDIR="$DSTROOT"/usr/share/man/man"$SECTION" + shift + for LINK in "$@"; do + ln -hf "$MANDIR/$MANPAGE" "$MANDIR/$LINK" + done +} + +InstallManPages removefile.3 +LinkManPages removefile.3 \ + removefile_state_alloc.3 \ + removefile_state_free.3 \ + removefile_state_get.3 \ + removefile_state_set.3 + +InstallManPages checkint.3 +LinkManPages checkint.3 \ + check_{,u}int{32,64}_{add,sub,mul,div}.3 diff --git a/src/libremovefile/xcodescripts/removefile.xcconfig b/src/libremovefile/xcodescripts/removefile.xcconfig new file mode 100644 index 000000000..e03dac85b --- /dev/null +++ b/src/libremovefile/xcodescripts/removefile.xcconfig @@ -0,0 +1,16 @@ +#include "/Makefiles/CoreOS/Xcode/BSD.xcconfig" +#include "/AppleInternal/XcodeConfig/SimulatorSupport.xcconfig" + +// Set INSTALL_PATH_ACTUAL to whatever INSTALL_PATH would normally be +INSTALL_PATH_ACTUAL = /usr/lib/system + +// Set INSTALL_PATH[sdk=macosx*] when SimulatorSupport.xcconfig is unavailable +INSTALL_PATH[sdk=macosx*] = $(INSTALL_PATH_ACTUAL) + +// Use $(INSTALL_PATH_PREFIX) instead of $(SDKROOT) as an unconditional prefix +PUBLIC_HEADERS_FOLDER_PATH = $(INSTALL_PATH_PREFIX)/usr/include +PRIVATE_HEADERS_FOLDER_PATH = $(INSTALL_PATH_PREFIX)/usr/local/include + +BUILD_VARIANTS = normal debug + +PRODUCT_NAME = removefile diff --git a/src/libsystem/CMakeLists.txt b/src/libsystem/CMakeLists.txt index 321f740e0..f9f00a426 100644 --- a/src/libsystem/CMakeLists.txt +++ b/src/libsystem/CMakeLists.txt @@ -38,7 +38,8 @@ SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) add_library(system SHARED ${libsystem_sources}) target_link_libraries(system system_malloc system_c system_kernel keymgr - system_m system_info system_notify unwind libdispatch_shared objc launch dyld) + system_m system_info system_notify unwind libdispatch_shared objc launch dyld + removefile) install(TARGETS system DESTINATION lib${SUFFIX}/darling)