libsmbclient-php
libsmbclient-php copied to clipboard
lseek with SEEK_CUR unexpectedly generates huge sparse files
Found this while writing testcases. This innocent-looking script will generate a 4.1GB sparse file on my machine:
<?php
// Open a file, write something:
$state = smbclient_state_new();
smbclient_state_init($state, null, 'testuser', 'password');
$file = smbclient_creat($state, 'smb://localhost/testshare/lseektest.txt');
smbclient_write($state, $file, 'abcdefgh');
// Seek 3 characters onwards:
smbclient_lseek($state, $file, 3, SEEK_CUR);
// Write some more, close the file:
smbclient_write($state, $file, 'foo', 3);
smbclient_close($state, $file);
smbclient_state_free($state);
Expected outcome: a file of 14 bytes in size: the characters 'abcdefgh', three zero bytes, and 'foo'.
I tried unsuccessfully to reproduce this behaviour with an equivalent c program, which leads me to conclude that this is a bug in libsmbclient-php somewhere.
#include <string.h>
#include <libsmbclient.h>
static void
smbclient_auth_func (SMBCCTX *ctx, const char *server, const char *share, char *wrkg, int wrkglen, char *user, int userlen, char *pass, int passlen)
{
strcpy(wrkg, "");
strcpy(user, "testuser");
strcpy(pass, "password");
}
int main ()
{
char *uri = "smb://localhost/testshare/lseektest.txt";
SMBCCTX *ctx = smbc_new_context();
smbc_setFunctionAuthDataWithContext(ctx, smbclient_auth_func);
smbc_init_context(ctx);
SMBCFILE *file = smbc_getFunctionCreat(ctx)(ctx, uri, 0666);
smbc_getFunctionWrite(ctx)(ctx, file, "abcdefgh", 8);
smbc_getFunctionLseek(ctx)(ctx, file, 3, SEEK_CUR);
smbc_getFunctionWrite(ctx)(ctx, file, "foo", 3);
smbc_getFunctionClose(ctx)(ctx, file);
smbc_free_context(ctx, 1);
}
Makefile:
test: test.c
$(CC) -lsmbclient -I/usr/include/samba-4.0 -o $@ $^
The c program outputs a file 11 bytes in size, containing the string 'abcdefghfoo'. Better, but also not what I expected.
More weirdness: in the PHP version, the lseek call returns '3'. In the c version, the lseek call returns '8'. Neither is correct from the documentation's point of view, since the return value should be the current absolute offset into the file (that is, '11').
- Under the hood, libsmbclient-php translates all three seek types into a
SEEK_SET. - An strace shows that the PHP code does indeed trigger an
fcntl64(11, F_GETLK64, {type=F_UNLCK, whence=SEEK_SET, start=4294967299, len=3, pid=0}) - This is a casting problem, but it's hard to pinpoint. I think it might have something to do with compiling this module within the PHP framework. For some odd reason, my copy of libsmbclient and my compiler appear to disagree about the type of
off_t(which is sorta worrying). However, not when I call the library from my c program.
Compiled within the PHP stack, this line (hand-patched in our libsmbclient.c) gives me the large negative offset:
if ((ret = smbc_lseek(state->ctx, file, 3, (int)whence)) > -1)
But the same line in my c program is fine.
- Could not reproduce in our Travis CI environment: the integration test that creates a 4.1GB file on my machine apparently gives proper file contents and offset sizes on Travis.