コード例 #1
0
ファイル: file_copy.c プロジェクト: neonatura/share
int share_file_copy_cb(SHFL *src_file, SHFL *dest_file)
{
  shpeer_t *src_peer = shfs_inode_peer(src_file);
  int err;

  if (0 == strcmp(src_peer->label, "file")) {
    char path[PATH_MAX+1];

    /* set link to local-disk path. */
    sprintf(path, "%s", shfs_inode_path(src_file));
    err = shfs_ext_set(src_file, path);
    if (err)
      return (err);
  }

  /* perform file copy */
  err = shfs_file_copy(src_file, dest_file);
  if (err) {
    fprintf(sharetool_fout, "%s: error copying \"%s\" to \"%s\": %s [sherr %d].\n",
        process_path, shfs_filename(src_file), shfs_filename(dest_file),
        sherrstr(err), err);
    return (err);
  }

  if (!(run_flags & PFLAG_QUIET) && (run_flags & PFLAG_VERBOSE)) {
    fprintf(sharetool_fout, "%s: %s \"%s\" copied to %s \"%s\".\n",
        process_path,
        shfs_type_str(shfs_type(src_file)), shfs_filename(src_file),
        shfs_type_str(shfs_type(dest_file)), shfs_filename(dest_file)); 
  }

  return (0);
}
コード例 #2
0
ファイル: shfs_rev.c プロジェクト: neonatura/share
shfs_ino_t *shfs_rev_get(shfs_ino_t *repo, shkey_t *rev_key)
{
  shfs_ino_t *rev;

  if (shfs_type(repo) == SHINODE_FILE)
    repo = shfs_inode(repo, NULL, SHINODE_REPOSITORY);
  if (shfs_type(repo) != SHINODE_REPOSITORY)
    return (NULL);

  if (shkey_cmp(rev_key, ashkey_blank()))
    return (NULL);

  return (shfs_inode(repo, (char *)shkey_hex(rev_key), SHINODE_REVISION));
}
コード例 #3
0
ファイル: shfs_rev.c プロジェクト: neonatura/share
int shfs_rev_init(shfs_ino_t *file)
{
  shfs_attr_t attr;
  shfs_ino_t *repo;
  shfs_ino_t *head;
  shfs_ino_t *rev;
  shfs_ino_t *tag;
  int err;

  if (shfs_type(file) != SHINODE_FILE)
    return (SHERR_OPNOTSUPP);

  attr = shfs_attr(file);
  if (attr & SHATTR_VER) {
    /* inode is already initialized for repository. */
    return (0);
  }

  /* commit current data content */
  err = shfs_rev_commit(file, &rev);
  if (err)
    return (err);

  /* set initial description */
  shfs_rev_desc_set(rev, "initial revision");

  /* create master branch */
  err = shfs_rev_branch(file, "master", rev);
  if (err)
    return (err);

  return (0);
}
コード例 #4
0
ファイル: shfs_rev.c プロジェクト: neonatura/share
/** reference a revision by name */
int shfs_rev_ref(shfs_ino_t *file, char *group, char *name, shfs_ino_t *rev)
{
  shfs_ino_t *branch;
  shfs_ino_t *ref;
  shkey_t *ref_key;
  char buf[SHFS_PATH_MAX];
  int err;

  if (!rev) {
    return (SHERR_INVAL);
  }

  if (shfs_type(rev) != SHINODE_REVISION)
    return (SHERR_INVAL);

  ref_key = shkey_hexgen(shfs_filename(rev));
  if (!ref_key)
    return (SHERR_IO);

  if (shkey_cmp(ref_key, ashkey_blank())) {
    /* not a revision (filename has no hex key) */
    shkey_free(&ref_key);
    return (SHERR_INVAL);
  }

  memset(buf, 0, sizeof(buf));
  snprintf(buf, sizeof(buf)-1, "%s/%s", group, name);
  err = shfs_obj_set(file, buf, ref_key);
  shkey_free(&ref_key);
  if (err)
    return (err);

  return (0);
}
コード例 #5
0
ファイル: shfs_ref.c プロジェクト: neonatura/share
int shfs_ref_get(shfs_ino_t *file,
                 shfs_t **ref_fs_p, shfs_ino_t **ref_p)
{
    shfs_ino_t *ref;
    shfs_ino_t *parent;
    shpeer_t *peer;
    shfs_t *fs;
    shkey_t *hier;
    shbuf_t *buff;
    char path[SHFS_PATH_MAX];
    int hier_cnt;
    int err;
    int i;

    *ref_p = NULL;
    *ref_fs_p = NULL;

    if (!file || !file->tree)
        return (SHERR_INVAL);

    buff = shbuf_init();
    err = _shfs_ref_raw_read(file, buff);
    if (err)
        return (err);

    peer = (shpeer_t *)shbuf_data(buff);
    hier = (shkey_t *)(shbuf_data(buff) + sizeof(shpeer_t));

    fs = shfs_init(peer);
    if (!fs)
        return (SHERR_IO);

    memset(path, 0, sizeof(path));
    strcpy(path, "/");
    ref = fs->fsbase_ino;
    for (i = SHFS_MAX_REFERENCE_HIERARCHY - 1; i >= 0; i--) {
        if (shkey_cmp(&hier[i], ashkey_blank()))
            continue;
        if (shkey_cmp(&hier[i], shfs_token(file->tree->fsbase_ino)))
            continue;

        ref = shfs_inode_load(ref, &hier[i]);
        if (!ref) {
            shfs_free(&fs);
            return (SHERR_NOENT);
        }

        if (shfs_type(ref) == SHINODE_DIRECTORY)
            strncat(path, "/", SHFS_PATH_MAX - strlen(path) - 1);
        strncat(path, shfs_filename(ref), SHFS_PATH_MAX - strlen(path) - 1);
    }

    shbuf_free(&buff);

    *ref_p = ref;
    *ref_fs_p = fs;

    return (0);
}
コード例 #6
0
ファイル: shfs_rev.c プロジェクト: neonatura/share
int shfs_rev_diff(shfs_ino_t *file, shkey_t *rev_key, shbuf_t *buff)
{
  shbuf_t *work_buff;
  shbuf_t *head_buff;
  shfs_ino_t *new_rev;
  shfs_ino_t *delta;
  shfs_ino_t *rev;
  shfs_t *fs;
  int err;

  if (!file || shfs_type(file) != SHINODE_FILE)
    return (SHERR_INVAL);

  if (!buff)
    return (SHERR_INVAL);

  if (!rev_key) {
    /* obtain current committed revision. */
    rev = shfs_rev_base(file);
  } else {
    rev = shfs_rev_get(file, rev_key);
  }
  if (!rev)
    return (SHERR_NOENT);

 /* obtain work-data for BASE branch revision. */
  head_buff = shbuf_init();
  err = shfs_rev_ref_read(file, "tag", "BASE", head_buff);
  if (err) {
    shbuf_free(&head_buff);
    return (err);
  }

  work_buff = shbuf_init();
  err = shfs_read(file, work_buff);
  if (err) {
    shbuf_free(&head_buff);
    shbuf_free(&work_buff);
    return (err);
  }

  if (shbuf_size(work_buff) == shbuf_size(head_buff) &&
      0 == memcmp(shbuf_data(work_buff), 
        shbuf_data(head_buff), shbuf_size(work_buff))) {
    /* no difference to report */
    err = 0;
  } else {
    /* print textual difference to <buff> */
    err = shdiff(buff, shbuf_data(work_buff), shbuf_data(head_buff));
  }
  shbuf_free(&work_buff);
  shbuf_free(&head_buff);

  return (err);
}
コード例 #7
0
ファイル: shfs_ref.c プロジェクト: neonatura/share
/**
 * @param file The inode refeferencing another inode.
 * @param ref_file The inode being referenced.
 */
int shfs_ref_set(shfs_ino_t *file, shfs_ino_t *ref_file)
{
    shfs_ino_t *parent;
    shbuf_t *buff;
    int err;
    int i;

    if (!file || !file->tree)
        return (SHERR_INVAL);

    if (shfs_type(file) != shfs_type(ref_file)) {
        if (shfs_type(ref_file) != SHINODE_DIRECTORY)
            return (SHERR_ISDIR);
        return (SHERR_NOTDIR);
    }

    buff = shbuf_init();
    shbuf_cat(buff, &file->tree->peer, sizeof(shpeer_t));

    parent = ref_file;
    for (i = 0; i < SHFS_MAX_REFERENCE_HIERARCHY; i++) {
        if (parent) {
            shbuf_cat(buff, &parent->blk.hdr.name, sizeof(shkey_t));
            parent = shfs_inode_parent(parent);
        }
    }

    err = _shfs_ref_raw_write(file, buff);
    shbuf_free(&buff);
    if (err)
        return (err);

    err = shfs_inode_write_entity(file);
    if (err)
        return (err);

    return (0);
}
コード例 #8
0
ファイル: shfs_lock.c プロジェクト: neonatura/share
int shfs_lock_of(shfs_ino_t *inode, int flags, size_t of, size_t len)
{
  shstat st;
  int err;

  err = shfs_fstat(inode, &st);
  if (err)
    return (err);

  if (shfs_type(inode) == SHINODE_FILE_LOCK)
    return (SHERR_OPNOTSUPP);

  return (shfs_attr_set(inode, SHATTR_FLOCK));
}
コード例 #9
0
ファイル: shfs_lock.c プロジェクト: neonatura/share
int shfs_unlock(shfs_ino_t *inode)
{
  shstat st;
  int err;

  err = shfs_fstat(inode, &st);
  if (err)
    return (err);

  if (shfs_type(inode) == SHINODE_FILE_LOCK)
    return (SHERR_OPNOTSUPP);

  return (shfs_attr_unset(inode, SHATTR_FLOCK));
}
コード例 #10
0
ファイル: shfs_rev.c プロジェクト: neonatura/share
int shfs_rev_delta(shfs_ino_t *file, shbuf_t *diff_buff)
{
  shstat st;
  shbuf_t *work_buff;
  shbuf_t *head_buff;
  shbuf_t *ref_buff;
  shfs_t *fs;
  shkey_t *key;
  int err;

  if (shfs_type(file) != SHINODE_FILE)
    return (SHERR_INVAL);

  err = shfs_fstat(file, &st);
  if (err)
    return (err);

  work_buff = shbuf_init();
  err = shfs_read(file, work_buff);
  if (err) {
    shbuf_free(&work_buff);
    return (err);
  }

  /* obtain BASE branch snapshot */
  head_buff = shbuf_init();
  err = shfs_rev_ref_read(file, "tag", "BASE", head_buff);
  if (err)
    goto done;

  if (shbuf_size(work_buff) == shbuf_size(head_buff) &&
      0 == memcmp(shbuf_data(work_buff), shbuf_data(head_buff), shbuf_size(work_buff))) {
    /* no difference */
    err = SHERR_AGAIN;
    goto done;
  }

  err = shdelta(work_buff, head_buff, diff_buff); 

done:
  shbuf_free(&work_buff);
  shbuf_free(&head_buff);
  shbuf_free(&work_buff);

  return (err);
}
コード例 #11
0
ファイル: shfs_lock.c プロジェクト: neonatura/share
static int _shfs_lock_verify_hier(shfs_ino_t *inode)
{
  shfs_t *parent;
  int err;

  if (shfs_type(inode) == SHINODE_FILE_LOCK)
    return (SHERR_OPNOTSUPP);

  if ((shfs_attr(inode) & SHATTR_FLOCK)) {
    return (SHERR_ACCESS);
  }

  /* reference fs from parent for fresh inodes */
  parent = shfs_inode_parent(inode);
  if (parent) {
    err = _shfs_lock_verify_hier(parent);
    if (err)
      return (err);
  }

  return (0);
}
コード例 #12
0
int shfs_truncate(shfs_ino_t *file, shsize_t len)
{
  shfs_ino_t *bin;
  shfs_ino_t *aux;
  int err;

  if (!file)
    return (0);

  if (!IS_INODE_CONTAINER(shfs_type(file)))
    return (SHERR_INVAL); /* see shfs_inode_truncate() */

  switch (shfs_format(file)) {
    case SHINODE_NULL:
      return (SHERR_NOENT); /* no data content */

    case SHINODE_BINARY:
      bin = shfs_inode(file, NULL, SHINODE_BINARY);
      err = shfs_inode_truncate(bin, len);
      if (err)
        return (err);

      /* update file inode attributes */
      file->blk.hdr.mtime = bin->blk.hdr.mtime;
      file->blk.hdr.size = bin->blk.hdr.size;
      file->blk.hdr.crc = bin->blk.hdr.crc;
      err = shfs_inode_write_entity(file);
      if (err)
        return (err);

      break;

    default:
      return (SHERR_OPNOTSUPP);
  }

  return (0);
}
コード例 #13
0
ファイル: shfs_rev.c プロジェクト: neonatura/share
int shfs_rev_checkout(shfs_ino_t *file, shkey_t *key, shfs_ino_t **rev_p)
{
  shfs_ino_t *repo;
  shfs_ino_t *rev;
  int err;

  if (!file || shfs_type(file) != SHINODE_FILE)
    return (SHERR_INVAL);

  repo = shfs_inode(file, NULL, SHINODE_REPOSITORY);
  rev = shfs_rev_get(repo, key);
  if (!rev)
    return (SHERR_NOENT);

  err = shfs_rev_base_set(file, rev);
  if (err)
    return (err);

  if (rev_p)
    *rev_p = rev;

  return (0);
}
コード例 #14
0
ファイル: file_copy.c プロジェクト: neonatura/share
int share_file_copy(char **args, int arg_cnt, int pflags)
{
  shfs_t *dest_fs;
  shfs_t *src_fs;
  shfs_ino_t *dest_file;
  shfs_ino_t *src_file;
  shbuf_t *buff;
  char fpath[PATH_MAX+1];
  unsigned char *data;
  size_t data_len;
  size_t of;
  int total;
  int src_cnt;
  int w_len;
  int err;
  int i;

  if (arg_cnt < 2)
    return (SHERR_INVAL);

  arg_cnt--;
  dest_fs = shfs_uri_init(args[arg_cnt], O_CREAT, &dest_file);
  if (!dest_fs)
    return (SHERR_IO);

  total = 0;
  for (i = 1; i < arg_cnt; i++) {
    err = share_file_stat_recursive(dest_file, args[i], &total);
    if (err)
      break;
  }
  if (err) {
    shfs_free(&dest_fs);
    return (err);
  }

  if (total == 0) {
    /* no matches */
    shfs_free(&dest_fs);
    return (SHERR_INVAL);
  }
  if (total > 1 &&
      shfs_type(dest_file) != SHINODE_DIRECTORY) {
    /* cannot copy multiple files to a single file */
    shfs_free(&dest_fs);
    return (SHERR_NOTDIR);
  }

err = 0;
  for (i = 1; i < arg_cnt; i++) {
    err = share_file_copy_recursive(dest_file, args[i]);
    if (err)
      break;
  }
  if (err) {
    shfs_free(&dest_fs);
    return (err);
  }

  shfs_free(&dest_fs);
  return (0);
}
コード例 #15
0
int shfs_file_remove(shfs_ino_t *file)
{
  shfs_ino_t *child;
  shbuf_t *buff;
  shfs_dirent_t *ents;
  size_t ent_max;
  int fmt;
  int err;
  int i;

  if (IS_INODE_CONTAINER(shfs_type(file))) {
    ents = NULL;
    ent_max = shfs_list(file, NULL, &ents);
    if (ent_max < 0) {
      return (ent_max);
    }

    if (ents) {
      for (i = 0; i < ent_max; i++) {
        if (IS_INODE_CONTAINER(ents[i].d_type)) {
          child = shfs_inode(file, ents[i].d_name, ents[i].d_type);
          err = shfs_file_remove(child);
          if (err)
            return (err);
        } else {
          child = shfs_inode(file, NULL, ents[i].d_type);
          shfs_inode_clear(child);
        }
      }
      free(ents);
    } else {
      /* version repository */
      if (shfs_attr(file) & SHATTR_VER) {
        child = shfs_inode(file, NULL, SHINODE_REPOSITORY);
        err = shfs_file_remove(child);
        if (err)
          return (err);
      }

      /* specific format */
      fmt = shfs_format(file);
      if (fmt == SHINODE_NULL && shfs_type(file) == SHINODE_BINARY) {
        fmt = SHINODE_AUX;
      }
      if (fmt != SHINODE_NULL) {
        child = shfs_inode(file, NULL, fmt);
        if (!IS_INODE_CONTAINER(shfs_type(child))) {
          err = shfs_inode_clear(child);
        } else {
          err = shfs_file_remove(child);
        }
        if (err)
          return (err);
      }
    }
  }

  if (shfs_type(file) != SHINODE_DIRECTORY) {
#if 0
/* DEBUG: perform inode_clear on 'fpos' index */
  /* clear previous format */
err = shfs_format_set(file, SHINODE_NULL);
        if (err)
          return (err);
#endif
  

    /* reset stats on file inode. */
    file->blk.hdr.mtime = 0;
    file->blk.hdr.size = 0;
    //  file->blk.hdr.type = SHINODE_NULL;
    file->blk.hdr.format = SHINODE_NULL;
    file->blk.hdr.attr = SHINODE_NULL;
    file->blk.hdr.crc = 0;
    err = shfs_inode_write_entity(file);
    if (err) {
      return (err);
    }

  }

  return (0);
}
コード例 #16
0
ファイル: shfs_rev.c プロジェクト: neonatura/share
/**
 * Generates the working-copy for a particular revision.
 */
int shfs_rev_read(shfs_ino_t *i_rev, shbuf_t *buff)
{
  shfs_ino_t *repo;
  shfs_ino_t *file;
  shfs_ino_t *rev;
  shbuf_t *delta_buff;
  shbuf_t *head_buff;
  shbuf_t *out_buff;
  int err;

  if (shfs_type(i_rev) != SHINODE_REVISION) {
    return (SHERR_INVAL);
  }

  repo = shfs_inode_parent(i_rev);
  if (!repo || shfs_type(repo) != SHINODE_REPOSITORY)
    return (SHERR_IO);

  file = shfs_inode_parent(repo);
  if (!file || shfs_type(file) != SHINODE_FILE)
    return (SHERR_IO);

  head_buff = shbuf_init();
  err = shfs_rev_ref_read(file, "tag", "BASE", head_buff);
  if (err)
    return (err);

  err = SHERR_NOENT; /* ret'd when no matching revision found */
  out_buff = shbuf_init();
  delta_buff = shbuf_init();

  /* search BASE chain for revision -- applying each revision's patch. */
  rev = shfs_rev_tag_resolve(file, "BASE");
  while (rev) {
    if (shkey_cmp(shfs_token(rev), shfs_token(i_rev))) {
      /* found revision in branch chain. */
      shbuf_append(head_buff, buff);
      err = 0;
      break;
    }

    shbuf_clear(delta_buff);
    err = shfs_rev_delta_read(rev, delta_buff);
    if (err)
      break;

/* DEBUG: TODO: merge together deltas to reduce performance over-head */
    shbuf_clear(out_buff);
    err = shpatch(head_buff, delta_buff, out_buff); 
    if (err)
      break;

    shbuf_clear(head_buff);
    shbuf_append(out_buff, head_buff);

    rev = shfs_rev_prev(rev);
  }

  shbuf_free(&head_buff);
  shbuf_free(&out_buff);
  shbuf_free(&delta_buff);
  return (err);
}
コード例 #17
0
int shfs_file_copy(shfs_ino_t *src_file, shfs_ino_t *dest_file)
{
  shfs_t *ref_fs;
  shfs_ino_t *ref;
  shstat st;
  shbuf_t *buff;
  int err;

  if (!src_file || !dest_file)
    return (SHERR_INVAL);

  /* ensure there is something to copy */
  err = shfs_fstat(src_file, &st);
  if (err) {
fprintf(stderr, "DEBUG: shfs_file_copy: %d = shfs_fstat(src_file)\n", err);
    return (err);
  }

  if (shfs_type(dest_file) == SHINODE_DIRECTORY) {

#if 0
    /* extract tar archive */
    if (shfs_format(dest_file) == SHINODE_BINARY &&
        0 == strcmp(shfs_meta_get(dest_file, "content.mime"), 
          "application/x-tar")) {
      buff = shbuf_init();
      err = shfs_read(src_file, buff);
      if (err) {
        shbuf_free(&buff);
        return (err);
      }
      err = shfs_unarch(buff, dest_file);
      shbuf_free(&buff);
      return (0);
    }
#endif

    if (!(shfs_attr(src_file) & SHATTR_ARCH)) {
      if (IS_INODE_CONTAINER(shfs_type(src_file))) {
        dest_file = shfs_inode(dest_file, 
            shfs_filename(src_file), shfs_type(src_file));
      } else {
        dest_file = shfs_inode(dest_file, NULL, shfs_type(src_file));
      }
    }

  }

  ref =  NULL;
  ref_fs = NULL;
  if (shfs_format(dest_file) == SHINODE_REFERENCE) {
    /* apply operation to end-point inode. */
    err = shfs_ref_get(dest_file, &ref_fs, &ref);
    if (err) {
fprintf(stderr, "DEBUG: shfs_file_copy: %d = shfs_ref_get(dest_file)\n", err); 
      return (err);
}

    dest_file = ref;
  }

  if (shfs_format(dest_file) != SHINODE_EXTERNAL) { 
    /* direct copy data content without conversion when applicable. */
    switch (shfs_format(src_file)) {
#if 0
      case SHINODE_COMPRESS:
        err = shfs_zlib_copy(src_file, dest_file);
        if (err)
          return (err);
        return (0);
#endif
    }
  }

  /* default case */
  buff = shbuf_init();
  err = shfs_read(src_file, buff);
  if (err) {
fprintf(stderr, "DEBUG: shfs_file_copy: %d = shfs_read()\n", err); 
    goto done;
}

  err = shfs_write(dest_file, buff);
  shbuf_free(&buff);
  if (err) {
fprintf(stderr, "DEBUG: shfs_file_copy: %d = shfs_write()\n", err);
    goto done;
}

  /* success */
  err = 0;

done:
  shbuf_free(&buff);
  if (ref_fs)
    shfs_free(&ref_fs);
  return (err);
}
コード例 #18
0
ファイル: shfs_attr.c プロジェクト: neonatura/share
int shfs_attr_set(shfs_ino_t *file, int attr)
{
  shfs_attr_t cur_flag;
  shmime_t *mime;
  int err_code;

  if (!file || !attr)
    return (SHERR_INVAL);

  cur_flag = shfs_attr(file);
  if (cur_flag & attr)
    return (0); /* already set */

  err_code = SHERR_OPNOTSUPP;
  switch (attr) {
    case SHATTR_ARCH:
      if (shfs_type(file) == SHINODE_DIRECTORY) { 
        err_code = 0;
      }
      break;
    case SHATTR_COMP:
      if (shfs_type(file) == SHINODE_DIRECTORY) { 
        /* files inherit */
        err_code = 0;
        break;
      }
      err_code = shfs_format_set(file, SHINODE_COMPRESS);
      break;
    case SHATTR_SYNC:
      err_code = 0;
      //err_code = shfs_file_notify(file);
      break;
    case SHATTR_TEMP:
      err_code = 0;
      break;
    case SHATTR_VER:
      err_code = shfs_rev_init(file);
      break;
    case SHATTR_READ:
    case SHATTR_WRITE:
    case SHATTR_EXE:
      err_code = 0;
      break;

    case SHATTR_DB:
      if (shfs_type(file) != SHINODE_FILE ||
          shfs_format(file) != SHINODE_NULL) {
        /* not a file or already contains content */
        err_code = SHERR_INVAL;
        break;
      }


err_code = 0;

#if 0
      err_code = shfs_format_set(file, SHINODE_DATABASE);
#endif




#if 0
      if (shfs_size(file) == 0) {
        /* new database. */
        err_code = 0;
      }

      mime = shmime_file(file);
      if (!mime || 0 == strcmp(mime->mime_name, SHMIME_APP_SQLITE)) {
        err_code = SHERR_INVAL;
        break;
      }
/* DEBUG: move to copy file area */
#endif

      break;
  }

  if (!err_code) {
    file->blk.hdr.attr |= attr;
    err_code = shfs_inode_write_entity(file);
  }

   
  if (!err_code && 
      ((file->blk.hdr.attr & SHATTR_SYNC) ||
       (cur_flag & cur_flag))) {
    /* notify share daemon of altered attribute state for synchronized inode. */
    shfs_file_notify(file);
  }

  return (err_code);
}
コード例 #19
0
ファイル: file_copy.c プロジェクト: neonatura/share
int share_file_copy_recursive(SHFL *dest_file, char *fname)
{
  shfs_dirent_t *ents;
  shfs_t *fs;
  SHFL **files;
  SHFL *dir;
  struct stat st;
  char spec_prefix[SHFS_PATH_MAX];
  char spec_dir[SHFS_PATH_MAX];
  char spec_fname[SHFS_PATH_MAX];
  char path[SHFS_PATH_MAX];
  char work_path[SHFS_PATH_MAX];
  char buf[4096];
char *list_fname;
  char *ptr;
  int ent_nr;
  int err;
  int i;

  memset(spec_prefix, 0, sizeof(spec_prefix));
  ptr = strstr(fname, ":/");
  if (ptr) {
    ptr += 2;
    strncpy(spec_prefix, fname, MIN(sizeof(spec_prefix)-1, (ptr-fname)));
    fname += strlen(spec_prefix);
  }

  if (!strchr(fname, '/') || 0 == strncmp(fname, "./", 2)) {
    if (0 == strncmp(fname, "./", 2)) 
      fname += 2;

    strcpy(spec_prefix, "file:/");
    getcwd(buf, sizeof(buf)-1);
    strcat(buf, "/");
    strcat(buf, fname);
    ptr = strrchr(buf, '/');
    strncpy(spec_dir, buf + 1, strlen(buf) - strlen(ptr));
    sprintf(spec_fname, "%s%s", spec_dir, ptr+1);

    list_fname = basename(spec_fname);
  } else {
    memset(spec_fname, 0, sizeof(spec_fname));
    strncpy(spec_fname, fname, sizeof(spec_fname)-1);
    list_fname = basename(spec_fname);

    memset(spec_dir, 0, sizeof(spec_dir));
    strncpy(spec_dir, fname, MIN(strlen(fname) - strlen(list_fname), sizeof(spec_dir)-1));
  }

  sprintf(path, "%s%s", spec_prefix, spec_dir); 
fprintf(stderr, "DEBUG: file_recursive: shfs_uri_init(%s)\n", path);
  fs = shfs_uri_init(path, 0, &dir);

  if (!*list_fname) {
    /* directory reference. */
    ent_nr = 1;
    files = (SHFL **)calloc(ent_nr+1, sizeof(SHFL *)); 
    if (!files) return (SHERR_NOMEM);
    files[0] = dir;
  } else {
    /* search files in directory */
    err = 0;
    ent_nr = shfs_list(dir, list_fname, &ents);
    if (ent_nr <= 0)
      return (ent_nr);

    err = SHERR_NOENT;
    files = (SHFL **)calloc(ent_nr+1, sizeof(SHFL *)); 
    if (!files) return (SHERR_NOMEM);
    for (i = 0; i < ent_nr; i++) {
      ptr = strstr(spec_dir, ":/");
      if (ptr) {
        sprintf(path, "%s%s", ptr+2, ents[i].d_name);
      } else {
        sprintf(path, "%s%s", spec_dir, ents[i].d_name);
      }
      err = shfs_stat(fs, path, &st);
      if (err)
        break;

      files[i] = shfs_file_find(fs, path);
    }
    shfs_list_free(&ents);
    if (err) {
      free(files);
      return (err);
    }
  }

#if 0
  /* handle recursive hierarchy */
  if ((run_flags & PFLAG_RECURSIVE)) {
    for (i = 0; i < ent_nr; i++) {
      if (shfs_type(files[i]) != SHINODE_DIRECTORY)
        continue;

      /* .. */
    }
  }
#endif
    
  for (i = 0; i < ent_nr; i++) {
    /* perform file copy */
    err = shfs_file_copy(files[i], dest_file);
    if (err) {
      fprintf(sharetool_fout, "%s: error copying \"%s\" to \"%s\": %s [sherr %d].\n",
          process_path, shfs_filename(files[i]), shfs_filename(dest_file),
          sherrstr(err), err);
      return (err);
    }

    if (!(run_flags & PFLAG_QUIET) && (run_flags & PFLAG_VERBOSE)) {
      fprintf(sharetool_fout, "%s: %s \"%s\" copied to %s \"%s\".\n",
        process_path,
        shfs_type_str(shfs_type(files[i])), shfs_filename(files[i]), 
        shfs_type_str(shfs_type(dest_file)), shfs_filename(dest_file)); 
    }
  }

  free(files);
  shfs_free(&fs);

  return (0);
}