Move Source From Main Repo

Based on removefile-48
This commit is contained in:
Thomas A 2023-02-19 18:22:38 -08:00
commit 5f47478822
17 changed files with 2832 additions and 0 deletions

26
CMakeLists.txt Normal file
View File

@ -0,0 +1,26 @@
project(libremovefile)
cmake_minimum_required(VERSION 3.10)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fblocks")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib")
add_definitions(-w -nostdinc)
set(removefile_sources
removefile.c
removefile_random.c
removefile_rename_unlink.c
removefile_sunlink.c
removefile_tree_walker.c
)
set(DYLIB_INSTALL_NAME "/usr/lib/system/libremovefile.dylib")
add_darling_library(removefile SHARED ${removefile_sources})
target_link_libraries(removefile system_c system_kernel system_dyld system_malloc compiler_rt)
make_fat(removefile)
install(TARGETS removefile DESTINATION libexec/darling/usr/lib/system)

26
LICENSE Normal file
View File

@ -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.

204
checkint.3 Normal file
View File

@ -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_<type>_<operation> "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_<type>_<operation> "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_<type>_<operation>
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_<type>_<operation>
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.

565
checkint.h Normal file
View File

@ -0,0 +1,565 @@
/*
* 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 <stdint.h>
#include <limits.h>
__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))
#define __CHECKINT_UNLIKELY(X) __builtin_expect((X),0)
__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 (__CHECKINT_UNLIKELY(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 (__CHECKINT_UNLIKELY((x & 0xffffffff00000000ull) || (y & 0xffffffff00000000ull))) *err |= CHECKINT_OVERFLOW_ERROR;
if (__CHECKINT_UNLIKELY(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 (__CHECKINT_UNLIKELY(x < LLONG_MIN - y)) *err |= CHECKINT_OVERFLOW_ERROR;
/* else, both arguments positive */
} else {
if (__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY(((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(__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY((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(__CHECKINT_UNLIKELY(((x < 0 && y >= 0) || (x >= 0 && y < 0)) && (x + y) < 0))
*err = *err | CHECKINT_OVERFLOW_ERROR;
else if(__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY((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 (__CHECKINT_UNLIKELY(x < INT32_MIN || x > INT32_MAX || y < INT32_MIN || y > INT32_MAX)) {
*err |= CHECKINT_OVERFLOW_ERROR;
}
int64_t z = x - y;
if (__CHECKINT_UNLIKELY(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 (__CHECKINT_UNLIKELY((x & 0xffffffff00000000ull) || (y & 0xffffffff00000000ull))) *err |= CHECKINT_OVERFLOW_ERROR;
if (__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY(x > LLONG_MAX + y))
*err = *err | CHECKINT_OVERFLOW_ERROR;
}
/* Negative x subtract a positive y */
else
{
if(__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY(x > y && ((x - y) > LLONG_MAX)))
*err = *err | CHECKINT_OVERFLOW_ERROR;
else if(__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY(((x < 0 && y <= 0) || (x >= 0 && y > 0)) && (x - y) < 0))
*err = *err | CHECKINT_OVERFLOW_ERROR;
else if(__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY(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 (__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY(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 (__CHECKINT_UNLIKELY(x < INT32_MIN || x > INT32_MAX || y < INT32_MIN || y > INT32_MAX)) {
*err |= CHECKINT_OVERFLOW_ERROR;
}
if (__CHECKINT_UNLIKELY(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 (__CHECKINT_UNLIKELY((x & 0xffffffff00000000ull) || (y & 0xffffffff00000000ull))) *err |= CHECKINT_OVERFLOW_ERROR;
if (__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY(LLONG_MAX/x < y))
*err = *err | CHECKINT_OVERFLOW_ERROR;
}
else
{
if(__CHECKINT_UNLIKELY(x == LLONG_MIN || y == LLONG_MIN))
*err = *err | CHECKINT_OVERFLOW_ERROR;
if(__CHECKINT_UNLIKELY(LLONG_MAX/(-x) < (-y)))
*err = *err | CHECKINT_OVERFLOW_ERROR;
}
}
else
{
if(x < 0)
{
if(__CHECKINT_UNLIKELY(x < LLONG_MIN/y))
*err = *err | CHECKINT_OVERFLOW_ERROR;
}
else if(__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY((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(__CHECKINT_UNLIKELY((x == INT_MIN) && y == -1)) {
*err = *err | CHECKINT_OVERFLOW_ERROR;
return 0;
}
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(__CHECKINT_UNLIKELY(x > ((uint32_t) INT_MAX + 1) && y == -1))
*err = *err | CHECKINT_OVERFLOW_ERROR;
else if(__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY((x == LLONG_MIN) && y == -1)) {
*err = *err | CHECKINT_OVERFLOW_ERROR;
return 0;
}
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(__CHECKINT_UNLIKELY(x > ((uint64_t) LLONG_MAX + 1) && y == -1))
*err = *err | CHECKINT_OVERFLOW_ERROR;
else if(__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY(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(__CHECKINT_UNLIKELY(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__ */

288
removefile.3 Normal file
View File

@ -0,0 +1,288 @@
.\" Copyright (c) 2015 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 Feb 26, 2015
.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_CROSS_MOUNT
By default, recursive traversals do not cross mount points. This option allows
.Fn removefile
to descend into directories that have a different device number than the file from which
the descent began.
.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 unlink 1 ,
.Xr sync 2 ,
.Xr sync_volume_np 3
.Sh HISTORY
The
.Fn removefile
API was introduced in Mac OS X 10.5.

144
removefile.c Normal file
View File

@ -0,0 +1,144 @@
#include "removefile.h"
#include "removefile_priv.h"
#include <TargetConditionals.h>
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, error = 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);
error = state->error_num;
free(paths[0]);
} else {
error = ENOMEM;
res = -1;
}
// deallocate if allocated locally
if (state_param == NULL) {
removefile_state_free(state);
}
if (res) {
errno = error;
}
return res;
}
int
removefile_cancel(removefile_state_t state) {
if (state == NULL) {
errno = EINVAL;
return -1;
} else {
state->cancelled = 1;
return 0;
}
}

71
removefile.h Normal file
View File

@ -0,0 +1,71 @@
/*
* Copyright (c) 2015 Apple Computer, Inc. All rights reserved.
*/
#ifndef __REMOVEFILE_H__
#define __REMOVEFILE_H__
#include <stdint.h>
#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
REMOVEFILE_CROSS_MOUNT = (1 << 7), // Cross mountpoints when deleting recursively.
};
/*
* 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__ */

22
removefile.plist Normal file
View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>OpenSourceImportDate</key>
<string>2004-09-22</string>
<key>OpenSourceLicense</key>
<string>MIT</string>
<key>OpenSourceLicenseFile</key>
<string>removefile.txt</string>
<key>OpenSourceProject</key>
<string>srm</string>
<key>OpenSourceSHA1</key>
<string>8b7aafc95b3ef67e2c737030567222bf50082188</string>
<key>OpenSourceURL</key>
<string>http://prdownloads.sourceforge.net/srm/srm-1.2.8.tar.gz?download</string>
<key>OpenSourceVersion</key>
<string>1.2.8</string>
<key>OpenSourceWebsiteURL</key>
<string>http://sourceforge.net/projects/srm/</string>
</dict>
</plist>

View File

@ -0,0 +1,275 @@
// !$*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 = (Private, ); }; };
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 = "<group>"; };
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 = "<group>"; };
FCCE179013592780002CEE6D /* checkint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = checkint.h; sourceTree = "<group>"; };
FCCE179113592780002CEE6D /* removefile_priv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = removefile_priv.h; sourceTree = "<group>"; };
FCCE179213592780002CEE6D /* removefile_random.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = removefile_random.c; sourceTree = "<group>"; };
FCCE179313592780002CEE6D /* removefile_rename_unlink.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = removefile_rename_unlink.c; sourceTree = "<group>"; };
FCCE179413592780002CEE6D /* removefile_sunlink.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = removefile_sunlink.c; sourceTree = "<group>"; };
FCCE179513592780002CEE6D /* removefile_tree_walker.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = removefile_tree_walker.c; sourceTree = "<group>"; };
FCCE179613592780002CEE6D /* removefile.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = removefile.3; sourceTree = "<group>"; };
FCCE179713592780002CEE6D /* removefile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = removefile.c; sourceTree = "<group>"; };
FCCE179813592780002CEE6D /* removefile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = removefile.h; sourceTree = "<group>"; };
FCCE17A5135A3E9C002CEE6D /* install_files.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = install_files.sh; path = xcodescripts/install_files.sh; sourceTree = "<group>"; };
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 = "<group>"; };
/* 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 = "<group>";
};
FCCE178A135924FC002CEE6D /* Products */ = {
isa = PBXGroup;
children = (
FCCE1789135924FC002CEE6D /* libremovefile.dylib */,
FCCE17D3135A6961002CEE6D /* test-removefile */,
);
name = Products;
sourceTree = "<group>";
};
/* 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 = {
SUPPORTS_TEXT_BASED_API = YES;
TAPI_VERIFY_MODE = Pedantic;
};
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;
IS_ZIPPERED = YES;
LINK_WITH_STANDARD_LIBRARIES = NO;
OTHER_LDFLAGS = (
"-Wl,-umbrella,System",
"-L/usr/lib/system",
"-ldyld",
"-lcompiler_rt",
"-lsystem$(SIM_SUFFIX)_kernel",
"-lsystem$(SIM_SUFFIX)_platform",
"-lsystem_malloc",
"-lsystem_c",
);
"SIM_SUFFIX[sdk=iphonesimulator*]" = _sim;
VERSION_INFO_PREFIX = "__attribute__((visibility(\"hidden\")))";
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 */;
}

67
removefile_priv.h Normal file
View File

@ -0,0 +1,67 @@
#ifdef __cplusplus
extern "C" {
#endif
#include "removefile.h"
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <fts.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
/*
* 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
#if __APPLE__
#ifndef AT_REMOVEDIR_DATALESS
#define AT_REMOVEDIR_DATALESS 0x0100 /* Remove a dataless directory without materializing first */
#endif
#endif

119
removefile_random.c Normal file
View File

@ -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 <fcntl.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#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
}

121
removefile_rename_unlink.c Normal file
View File

@ -0,0 +1,121 @@
/* 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 <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <TargetConditionals.h>
#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 __APPLE__
if (S_ISDIR(statbuf.st_mode) && (statbuf.st_flags & SF_DATALESS) != 0) {
return unlinkat(AT_FDCWD, path, AT_REMOVEDIR_DATALESS);
}
#endif
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);
}

456
removefile_sunlink.c Normal file
View File

@ -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 <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <time.h>
#include <unistd.h>
#if HAVE_LINUX_EXT2_FS_H
#include <linux/ext2_fs.h>
#endif
#if HAVE_SYS_VFS_H
#include <sys/vfs.h>
#endif
#if HAVE_SYS_PARAM_H && HAVE_SYS_MOUNT_H
#include <sys/param.h>
#include <sys/mount.h>
#endif
#if __APPLE__
#include <sys/disk.h>
#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);
}

259
removefile_tree_walker.c Normal file
View File

@ -0,0 +1,259 @@
/* srm */
/* Copyright (c) 2000 Matthew D. Gauthier
* Portions copyright (c) 2015 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 <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fts.h>
#include "removefile.h"
#include "removefile_priv.h"
static 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 {
#if __APPLE__
int is_dataless = (current_file->fts_statp->st_flags & SF_DATALESS) != 0;
if (is_dataless) {
int iopolicy = getiopolicy_np(IOPOL_TYPE_VFS_MATERIALIZE_DATALESS_FILES, IOPOL_SCOPE_THREAD);
int non_materializing = iopolicy == IOPOL_MATERIALIZE_DATALESS_FILES_OFF;
if (non_materializing || state->confirm_callback == NULL) {
res = unlinkat(AT_FDCWD, path, AT_REMOVEDIR_DATALESS);
} else {
res = rmdir(path);
}
} else {
res = rmdir(path);
}
#else
res = rmdir(path);
#endif
}
}
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;
int open_flags = 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;
open_flags = FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV;
/*
* Don't cross a mount point when deleting recursively by default.
* This default was changed in 10.11, previous to which there was
* no way to prevent removefile from crossing mount points.
* see: rdar://problem/6799948
*/
if ((REMOVEFILE_CROSS_MOUNT & state->unlink_flags) != 0)
open_flags &= ~FTS_XDEV;
stream = fts_open(trees, open_flags, NULL);
if (stream == NULL) {
state->error_num = errno;
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;
}
/* Setting FTS_XDEV skips the mountpoint on pre-order traversal,
* but you have to manually hop over it on post-order or
* removefile will return an error.
*/
if (current_file->fts_info == FTS_DP &&
stream->fts_options & FTS_XDEV &&
stream->fts_dev != current_file->fts_dev) {
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)) {
state->error_num = ECANCELED;
rval = -1;
}
fts_close(stream);
return rval;
}

136
test/test-removefile.c Normal file
View File

@ -0,0 +1,136 @@
#include "removefile.h"
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
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, "%ld.%03ld\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;
}
void 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;
if (argc == 2) {
/* pass in a directory with a mountpoint under it to test REMOVEFILE_CROSS_MOUNT */
state = removefile_state_alloc();
removefile_state_set(state, REMOVEFILE_STATE_ERROR_CALLBACK, removefile_error_callback);
removefile_state_set(state, REMOVEFILE_STATE_ERROR_CONTEXT, (void*)4567);
err = removefile(argv[1], state, REMOVEFILE_CROSS_MOUNT | REMOVEFILE_RECURSIVE);
return err;
}
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;
}

View File

@ -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

View File

@ -0,0 +1,10 @@
#include "<DEVELOPER_DIR>/Makefiles/CoreOS/Xcode/BSD.xcconfig"
INSTALL_PATH = /usr/lib/system
PUBLIC_HEADERS_FOLDER_PATH = /usr/include
PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include
BUILD_VARIANTS = normal debug
PRODUCT_NAME = removefile