Plan 9 from Bell Labs’s /usr/web/sources/contrib/de0u/root/sys/src/cmd/divergefs/filehandle.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.



#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include <String.h>
#include "common.h"
#include "debug.h"
#include "utils.h"
#include "string.h"
#include "collection.h"
#include "function.h"
#include "array.h"
#include "set.h"
#include "file.h"
#include "filepath.h"
#include "holemanager.h"
#include "fs.h"
#include "rule.h"
#include "filehandle.h"

enum { DEBUG_DIRECTORYHANDLE = false };

typedef void (*filehandlefree)(FileHandle *);

typedef struct FileHandleOperations
{
  /** closes the current handle after which the handle is unusable */
  void (*close)(FileHandle *handle);
  void (*read)(FileHandle *handle, Req *request, HoleManager *holemanager);
  void (*write)(FileHandle *handle, Req *request);
  filehandlefree free;
} FileHandleOperations;

struct FileHandle
{
  FileHandleOperations *ops;
};

void filehandle_free(FileHandle *self)
{
  if(self == nil)
  {
    return;
  }

  filehandle_close(self);
  assert_valid(self->ops->free);
  self->ops->free(self);
}

void filehandle_close(FileHandle *self)
{
  assert_valid(self);
  assert_valid(self->ops);
  assert_valid(self->ops->close);
  self->ops->close(self);
}

void filehandle_read(FileHandle *self, Req *request, HoleManager *holemanager)
{
  assert_valid(self);
  assert_valid(self->ops);
  assert_valid(self->ops->read);
  self->ops->read(self, request, holemanager);
}

void filehandle_write(FileHandle *self, Req *request)
{
  assert_valid(self);
  assert_valid(self->ops);
  assert_valid(self->ops->write);
  self->ops->write(self, request);
}

typedef struct SingleFileHandle
{
  FileHandle;
  int fd;
} SingleFileHandle;

static void singlefilehandle_close(FileHandle *handle);
static void singlefilehandle_read(
  FileHandle *handle, Req *request, HoleManager *holemanager);
static void singlefilehandle_write(FileHandle *handle, Req *request);

static FileHandleOperations singlefileops =
{
  .close  = singlefilehandle_close,
  .read   = singlefilehandle_read,
  .write  = singlefilehandle_write,
  .free   = (filehandlefree)free
};

SingleFileHandle *singlefilehandle_open(int fd)
{
  SingleFileHandle *result;
  assert(fd_isopen(fd));

  result = (SingleFileHandle *)emalloc_fs(sizeof(*result));
  result->ops = &singlefileops;
  result->fd = fd;
  return result;
} 

static void singlefilehandle_close(FileHandle *handle)
{
  SingleFileHandle *self = (SingleFileHandle *)handle;
  assert(fd_isopen(self->fd));
  file_close(self->fd);
  self->fd = INVALID_FD;
}

static void singlefilehandle_read(
  FileHandle *handle, Req *request, HoleManager *)
{
  long readcount;
  static char buffer[FS_READ_BUFFER_MAX];
  SingleFileHandle *self = (SingleFileHandle *)handle;
  assert(fd_isopen(self->fd));

  request->ofcall.count = 
    (request->ifcall.count > sizeof(buffer)) ?
      request->ifcall.count : sizeof(buffer);

  readcount =
    pread(self->fd, buffer, request->ofcall.count, request->ifcall.offset);
  return_9p_error_if(readcount < 0, last_error());
  request->ofcall.count = readcount;
  request->ofcall.data = buffer;
  return_9p_success();
}

static void singlefilehandle_write(FileHandle *handle, Req *request)
{
  long writecount;
  SingleFileHandle *self = (SingleFileHandle *)handle;
  assert(fd_isopen(self->fd));

  writecount = pwrite(self->fd,
    request->ifcall.data, request->ifcall.count, request->ifcall.offset);
  return_9p_error_if(writecount < 0, last_error());
  request->ofcall.count = writecount;
  return_9p_success();
}

typedef struct RuleFDPair
{
  Rule *rule;
  int fd;
} RuleFDPair;

static RuleFDPair *rulefdpair_new(Rule *rule, int fd)
{
  RuleFDPair *result;
  assert_valid(rule);
  assert(fd_isopen(fd));
  result = (RuleFDPair *)emallocz_fs(sizeof(*result));
  result->rule = rule;
  result->fd = fd;
  return result;
}

static void rulefdpair_free(RuleFDPair *self)
{
  if(self == nil)
  {
    return;
  }
  assert_valid(self->rule);
  assert(fd_isopen(self->fd));
  file_close(self->fd);
  free(self);
}

struct DirectoryHandle
{
  FileHandle;
  char *path;
  Array *rulefdpairs;
  Array *dirs;
};

static void directoryhandle_close(FileHandle *handle);
static void directoryhandle_read(
  FileHandle *handle, Req *request, HoleManager *holemanager);
static void directoryhandle_write(FileHandle *handle, Req *request);
static void directoryhandle_free(FileHandle *handle);

static FileHandleOperations directoryops =
{
  .close  = directoryhandle_close,
  .read   = directoryhandle_read,
  .free   = directoryhandle_free
};

DirectoryHandle *directoryhandle_new(char *path)
{
  DirectoryHandle *result;
  result = (DirectoryHandle *)emallocz_fs(sizeof(*result));
  result->ops = &directoryops;
  result->path = estrdup_fs(path);
  result->rulefdpairs = array_new();
  result->dirs = array_new();
  return result;
}

static void directoryhandle_free(FileHandle *handle)
{
  DirectoryHandle *self = (DirectoryHandle *)handle;
  free(self->path);
  assert(array_size(self->rulefdpairs) == 0);
  array_free(self->rulefdpairs);
  assert(array_size(self->dirs) == 0);
  array_free(self->dirs);
  free(self);
}

static void directoryhandle_close(FileHandle *handle)
{
  DirectoryHandle *self = (DirectoryHandle *)handle;

  array_clear_with(self->rulefdpairs, (functionunary)rulefdpair_free);
  array_clear_with(self->dirs, free);
}

static void directoryhandle_dir_add(DirectoryHandle *self, Dir *d)
{
  array_add(self->dirs, dir_clone(d));
}

typedef struct DirectoryHandleReadData 
{
  DirectoryHandle *self;
  Set *names;
  HoleManager *holemanager;
} DirectoryHandleReadData;

static void directoryhandle_dir_addall(
  DirectoryHandleReadData *data, RuleFDPair *pair)
{
  int i;
  int total;
  Dir *all;
  String *filepath;

  total = dirreadall(pair->fd, &all);
  if(total < 0)
  {
    return;
  }

  filepath = s_copy(data->self->path);
  for(i = 0; i < total; ++i)
  {
    filepath_append(&filepath, all[i].name);

    if( !set_includes(data->names, all[i].name) &&
        !holemanager_includes(data->holemanager, s_to_c(filepath)) &&
        rule_exists(pair->rule, s_to_c(filepath)))
    {
      directoryhandle_dir_add(data->self, &(all[i]));
      set_add(data->names, estrdup_fs(all[i].name));
    }

    filepath_remove_last(filepath);
  }
  free(all);
  s_free(filepath);
}

static collection_do_ret directoryhandle_read_each(void *p, void *arg)
{
  RuleFDPair *pair = (RuleFDPair *)p;
  DirectoryHandleReadData *data = (DirectoryHandleReadData *)arg;
  assert_valid(pair);
  assert_valid(data);

  directoryhandle_dir_addall(data, pair);
  return COLLECTION_DO_CONTINUE;
}

enum { DIRGEN_ERROR = -1, DIRGEN_SUCCESS = 0 };

static int directoryhandle_dirgen(int n, Dir *result, void *aux)
{
  DirectoryHandle *self = (DirectoryHandle *)aux;
  assert_valid(self);
  assert_valid(result);

  if(n < 0 || n >= array_size(self->dirs))
  {
    return DIRGEN_ERROR;
  }

  dir_copy((Dir *)(array_at(self->dirs, n)), result);
  return DIRGEN_SUCCESS;
}

static void directoryhandle_reread_if_offset_zero(
  DirectoryHandle *self, Req *request, HoleManager *holemanager)
{
  DirectoryHandleReadData data;

  if(request->ifcall.offset != 0)
  {
    return;
  }

  data.self = self;
  data.names = set_new(string_isequal, string_hash);
  data.holemanager = holemanager;
  array_unary_do(self->dirs, free);
  array_do(self->rulefdpairs, directoryhandle_read_each, &data);
  set_free_with(data.names, free); 
}

static void directoryhandle_read(
  FileHandle *handle, Req *request, HoleManager *holemanager)
{
  DirectoryHandle *self = (DirectoryHandle *)handle;

  directoryhandle_reread_if_offset_zero(self, request, holemanager);
  dirread9p(request, directoryhandle_dirgen, self);
  return_9p_success();
}

static void directoryhandle_write(FileHandle *, Req *)
{
  /* this should never happen */
  FATAL(DEBUG_DIRECTORYHANDLE, "directoryhandle_write should never be called");
  assert_valid(false);
}

uint directoryhandle_count(DirectoryHandle *self)
{
  assert_valid(self);
  return array_size(self->rulefdpairs);
}

void directoryhandle_add(DirectoryHandle *self, Rule *rule, int fd)
{
  assert_valid(self);
  assert_valid(rule);
  assert(fd_isopen(fd));
  array_add(self->rulefdpairs, rulefdpair_new(rule, fd));
}


Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.