Hello,

fdlength in lib.c does not seem to work properly, I noticed this when
toybox's insmod sometimes failed mysteriously.
I have attached a patch that should fix the issue, along with a test case
that evidences the issue (use -std=c99).

Regards,
Achille

Attachment: patch-fix-fdlength.diff
Description: Binary data

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Return how long the file at fd is, if there's any way to determine it.
off_t fdlength_old(int fd)
{
  off_t bottom = 0, top = 0, pos, old;
  int size;

  // If not, do a binary search for the last location we can read.

  old = lseek(fd, 0, SEEK_CUR);
  do {
    char temp;

    pos = bottom + (top - bottom) / 2;

    // If we can read from the current location, it's bigger.

    if (lseek(fd, pos, 0)>=0 && read(fd, &temp, 1)==1) {
      if (bottom == top) bottom = top = (top+1) * 2;
      else bottom = pos;

      // If we can't, it's smaller.

    } else {
      if (bottom == top) {
        if (!top) return 0;
        bottom = top/2;
      } else top = pos;
    }
  } while (bottom + 1 != top);

  lseek(fd, old, SEEK_SET);

  return pos + 1;
}

static bool testread(int fd, int pos)
{
  char temp;
  return lseek(fd, pos, SEEK_SET) >= 0 &&
    read(fd, &temp, 1) == 1;
}

int fdlength_new(int fd)
{
  int old = lseek(fd, 0, SEEK_CUR);

#ifdef CHEAT
  int n = lseek(fd, 0, SEEK_END);
  if (n >= 0) {
    lseek(fd, old, SEEK_SET);
    return n;
  }
#endif

  int bottom = 0, top = 0;

  // ensure top is not readable
  while (testread(fd, top)) {
    top = (top + 1) * 2; // XXX: might overflow
  }

  if (top == 0)
    return 0;

  for (;;) {
    int pos = bottom + (top - bottom) / 2;
    if (testread(fd, pos)) {
      if (pos + 1 == top) {
        lseek(fd, old, SEEK_SET);
        return top;
      }
      bottom = pos;
    } else {
      top = pos;
    }
  }
}

static const char zero_path[] = "/tmp/zero";

static int zero_create(int size)
{
  int fd = open(zero_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
  if (fd < 0)
    return fd;
  char block[4096];
  memset(block, 0, sizeof(block));
  while (size > 0) {
    int n = size <= (int)sizeof(block) ? size : (int)sizeof(block);
    write(fd, block, n);
    size -= n;
  }
  close(fd);

  return open(zero_path, O_RDONLY);
}

static void zero_delete()
{
  unlink(zero_path);
}

int main()
{
  for (int i = 0; i < 1024; ++i) {
    int fd = zero_create(i);
    if (fd < 0)
      return 1;
    int n_old = fdlength_old(fd);
    int n_new = fdlength_new(fd);
    assert(n_new == i);
    if (n_old != i)
      printf("length = %d, n_old = %d, n_new = %d\n", i, n_old, n_new);
    close(fd);
    zero_delete();
  }
  return 0;
}

_______________________________________________
Toybox mailing list
Toybox@lists.landley.net
http://lists.landley.net/listinfo.cgi/toybox-landley.net

Reply via email to