mirror of
https://github.com/darlinghq/darling-removefile.git
synced 2024-11-26 22:10:22 +00:00
Move Source From Main Repo
Based on removefile-48
This commit is contained in:
commit
5f47478822
26
CMakeLists.txt
Normal file
26
CMakeLists.txt
Normal 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
26
LICENSE
Normal 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
204
checkint.3
Normal 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
565
checkint.h
Normal 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
288
removefile.3
Normal 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
144
removefile.c
Normal 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
71
removefile.h
Normal 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
22
removefile.plist
Normal 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>
|
275
removefile.xcodeproj/project.pbxproj
Normal file
275
removefile.xcodeproj/project.pbxproj
Normal 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
67
removefile_priv.h
Normal 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
119
removefile_random.c
Normal 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
121
removefile_rename_unlink.c
Normal 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
456
removefile_sunlink.c
Normal 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
259
removefile_tree_walker.c
Normal 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
136
test/test-removefile.c
Normal 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;
|
||||
}
|
43
xcodescripts/install_files.sh
Normal file
43
xcodescripts/install_files.sh
Normal 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
|
10
xcodescripts/removefile.xcconfig
Normal file
10
xcodescripts/removefile.xcconfig
Normal 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
|
Loading…
Reference in New Issue
Block a user