eduardok / libsmbclient-php

smbclient's extension for PHP
Other
99 stars 21 forks source link

lseek with SEEK_CUR unexpectedly generates huge sparse files #9

Open aklomp opened 10 years ago

aklomp commented 10 years ago

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').

aklomp commented 10 years ago

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.