osg*_*sgx 6 c freebsd fseek large-files
我在FreeBSD上有旧的32位C/C++程序,它被数百名用户远程使用,其作者无法修复它.它是以不安全的方式编写的,所有文件偏移都在内部存储为无符号32位偏移和ftell/或使用的fseek函数.在FreeBSD 7(软件的主机平台)中,这意味着ftell和fseek使用32位有符号长:
int fseek(FILE *stream, long offset, int whence);
long ftell(FILE *stream);
Run Code Online (Sandbox Code Playgroud)
我需要快速修复程序,因为一些内部数据文件在收集数据13年后突然达到2 ^ 31文件大小(2 147 483 7yy字节),并且内部fseek/ftell断言现在对任何请求都失败了.
在FreeBSD7世界中,有2GB +文件的fseeko/ ftellohack.
int
fseeko(FILE *stream, off_t offset, int whence);
off_t
ftello(FILE *stream);
Run Code Online (Sandbox Code Playgroud)
off_t这里的类型没有明确定义; 我现在所知道的,它有8字节大小,看起来像long longOR unsigned long long(我不知道哪一个).
是否足够(高达4 GB的文件工作),安全搜索和替换所有ftell来ftello,和所有fseek对fseeko(sed -i 's/ftell/ftello',同为搜索),如果他们中的可能的用途是:
unsigned long offset1,offset2; //32bit
offset1 = (compute + it) * in + some - arithmetic;
fseek(file, 0, SEEK_END);
fseek(file, 4, SEEK_END); // or other small int constant
offset2 = ftell(file);
fseek(file, offset1, SEEK_SET); // No usage of SEEK_CUR
Run Code Online (Sandbox Code Playgroud)
和这些电话的组合.
off_t的签名是什么?将64位off_t分配到无符号32位偏移量是安全的吗?它适用于2 GB到4 GB范围内的字节吗?
哪些功能可用于处理偏移ftell/ fseek?
Nom*_*mal 12
FreeBSD fseeko()与ftello() POSIX.1-2001兼容,这意味着它off_t是一个有符号整数类型.
在FreeBSD 7上,你可以放心地做到:
off_t actual_offset;
unsigned long stored_offset;
if (actual_offset >= (off_t)0 && actual_offset < (off_t)4294967296.0)
stored_offset = (unsigned long)actual_offset;
else
some_fatal_error("Unsupportable file offset!");
Run Code Online (Sandbox Code Playgroud)
(在LP64架构,上面将是愚蠢的,因为off_t和long将两个是64位有符号整数这将是安全的,即使再;只是愚蠢,因为所有可能的文件偏移量可以支持)
人们经常被这种情况所困扰的是,必须使用完成偏移计算off_t.也就是说,它是不够的,投的结果来off_t,你必须转换values的算术来使用off_t.(从技术上讲,你只需要确保每个算术运算都是off_t精确完成的,但是我发现如果我只是试图并转换所有的操作数就更容易记住规则.)例如:
off_t offset;
unsigned long some, values, used;
offset = (off_t)some * (off_t)value + (off_t)used;
fseeko(file, offset, SEEK_SET);
Run Code Online (Sandbox Code Playgroud)
通常,偏移计算用于查找特定记录中的字段; 算术趋于保持不变.我真的建议你将搜索操作移动到辅助函数,如果可能的话:
int fseek_to(FILE *const file,
const unsigned long some,
const unsigned long values,
const unsigned long used)
{
const off_t offset = (off_t)some * (off_t)value + (off_t)used;
if (offset < (off_t)0 || offset >= (off_t)4294967296.0)
fatal_error("Offset exceeds 4GB; I must abort!");
return fseeko(file, offset, SEEK_SET);
}
Run Code Online (Sandbox Code Playgroud)
现在,如果你碰巧处于一个幸运的位置,你知道所有的偏移都是对齐的(对于某个整数,比如4),你可以给自己几年的时间来重写应用程序,通过使用以上:
#define BIG_N 4
int fseek_to(FILE *const file,
const unsigned long some,
const unsigned long values,
const unsigned long used)
{
const off_t offset = (off_t)some * (off_t)value + (off_t)used;
if (offset < (off_t)0)
fatal_error("Offset is negative; I must abort!");
if (offset >= (off_t)(BIG_N * 2147483648.0))
fatal_error("Offset is too large; I must abort!");
if ((offset % BIG_N) && (offset >= (off_t)2147483648.0))
fatal_error("Offset is not a multiple of BIG_N; I must abort!");
return fseeko(file, offset, SEEK_SET);
}
int fseek_big(FILE *const file, const unsigned long position)
{
off_t offset;
if (position >= 2147483648UL)
offset = (off_t)2147483648UL
+ (off_t)BIG_N * (off_t)(position - 2147483648UL);
else
offset = (off_t)position;
return fseeko(file, offset, SEEK_SET);
}
unsigned long ftell_big(FILE *const file)
{
off_t offset;
offset = ftello(file);
if (offset < (off_t)0)
fatal_error("Offset is negative; I must abort!");
if (offset < (off_t)2147483648UL)
return (unsigned long)offset;
if (offset % BIG_N)
fatal_error("Offset is not a multiple of BIG_N; I must abort!");
if (offset >= (off_t)(BIG_N * 2147483648.0))
fatal_error("Offset is too large; I must abort!");
return (unsigned long)2147483648UL
+ (unsigned long)((offset - (off_t)2147483648UL) / (off_t)BIG_N);
}
Run Code Online (Sandbox Code Playgroud)
逻辑很简单:如果offset小于2 31,则按原样使用.否则,它是表示由值2 31 + BIG_N ×(偏移 - 2 31).唯一的要求是偏移2 31及以上始终是BIG_N的倍数.
显然,你必须只使用上面三个函数 - 加上你需要的fseek_to()变体,只要它们做同样的检查,只需使用不同的参数和公式进行偏移计算 - 你可以支持文件大小高达2147483648 + BIG_N ×2147483647.对于BIG_N == 4,即10 GiB(少于4个字节;准确地说是10,737,418,236个字节).
有问题吗?
编辑澄清:
从更换您fseek(file, position, SEEK_SET)的电话开始fseek_pos(file, position),
static inline void fseek_pos(FILE *const file, const unsigned long position)
{
if (fseeko(file, (off_t)position, SEEK_SET))
fatal_error("Cannot set file position!");
}
Run Code Online (Sandbox Code Playgroud)
并fseek(file, position, SEEK_END)调用fseek_end(file, position)(对称 - 我假设这个位置通常是一个字面整数常量),
static inline void fseek_end(FILE *const file, const off_t relative)
{
if (fseeko(file, relative, SEEK_END))
fatal_error("Cannot set file position!");
}
Run Code Online (Sandbox Code Playgroud)
最后,ftell(file)打电话给ftell_pos(file):
static inline unsigned long ftell_pos(FILE *const file)
{
off_t position;
position = ftello(file);
if (position == (off_t)-1)
fatal_error("Lost file position!");
if (position < (off_t)0 || position >= (off_t)4294967296.0)
fatal_error("File position outside the 4GB range!");
return (unsigned long)position;
}
Run Code Online (Sandbox Code Playgroud)
由于您的体系结构和操作系统unsigned long是32位无符号整数类型并且off_t是64位有符号整数类型,因此这为您提供了完整的4GB范围.
对于偏移计算,定义一个或多个类似的函数
static inline void fseek_to(FILE *const file, const off_t term1,
const off_t term2,
const off_t term3)
{
const off_t position = term1 * term2 + term3;
if (position < (off_t)0 || position >= (off_t)4294967296.0)
fatal_error("File position outside the 4GB range!");
if (fseeko(file, position, SEEK_SET))
fatal_error("Cannot set file position!");
}
Run Code Online (Sandbox Code Playgroud)
对于每个偏移计算算法,定义一个fseek_to变体.命名参数以使算法有意义.const off_t如上所述制作参数,这样您就不需要在算术中使用额外的强制转换.只有参数和const off_t position =定义计算算法的行在变量函数之间变化.
有问题吗?