Beispiel #1
0
static tr_watchdir_status onFileAdded(tr_watchdir_t dir, char const* name, void* context)
{
    tr_session* session = context;

    if (!tr_str_has_suffix(name, ".torrent"))
    {
        return TR_WATCHDIR_IGNORE;
    }

    char* filename = tr_buildPath(tr_watchdir_get_path(dir), name, NULL);
    tr_ctor* ctor = tr_ctorNew(session);
    int err = tr_ctorSetMetainfoFromFile(ctor, filename);

    if (err == 0)
    {
        tr_torrentNew(ctor, &err, NULL);

        if (err == TR_PARSE_ERR)
        {
            tr_logAddError("Error parsing .torrent file \"%s\"", name);
        }
        else
        {
            bool trash = false;
            bool const test = tr_ctorGetDeleteSource(ctor, &trash);

            tr_logAddInfo("Parsing .torrent file successful \"%s\"", name);

            if (test && trash)
            {
                tr_error* error = NULL;

                tr_logAddInfo("Deleting input .torrent file \"%s\"", name);

                if (!tr_sys_path_remove(filename, &error))
                {
                    tr_logAddError("Error deleting .torrent file: %s", error->message);
                    tr_error_free(error);
                }
            }
            else
            {
                char* new_filename = tr_strdup_printf("%s.added", filename);
                tr_sys_path_rename(filename, new_filename, NULL);
                tr_free(new_filename);
            }
        }
    }
    else
    {
        err = TR_PARSE_ERR;
    }

    tr_ctorFree(ctor);
    tr_free(filename);

    return err == TR_PARSE_ERR ? TR_WATCHDIR_RETRY : TR_WATCHDIR_ACCEPT;
}
Beispiel #2
0
static void blocklistLoad(tr_blocklistFile* b)
{
    tr_sys_file_t fd;
    uint64_t byteCount;
    tr_sys_path_info info;
    char* base;
    tr_error* error = NULL;
    char const* err_fmt = _("Couldn't read \"%1$s\": %2$s");

    blocklistClose(b);

    if (!tr_sys_path_get_info(b->filename, 0, &info, NULL))
    {
        return;
    }

    byteCount = info.size;

    if (byteCount == 0)
    {
        return;
    }

    fd = tr_sys_file_open(b->filename, TR_SYS_FILE_READ, 0, &error);

    if (fd == TR_BAD_SYS_FILE)
    {
        tr_logAddError(err_fmt, b->filename, error->message);
        tr_error_free(error);
        return;
    }

    b->rules = tr_sys_file_map_for_reading(fd, 0, byteCount, &error);

    if (b->rules == NULL)
    {
        tr_logAddError(err_fmt, b->filename, error->message);
        tr_sys_file_close(fd, NULL);
        tr_error_free(error);
        return;
    }

    b->fd = fd;
    b->byteCount = byteCount;
    b->ruleCount = byteCount / sizeof(struct tr_ipv4_range);

    base = tr_sys_path_basename(b->filename, NULL);
    tr_logAddInfo(_("Blocklist \"%s\" contains %zu entries"), base, b->ruleCount);
    tr_free(base);
}
static void
logFunc (int severity, const char * message)
{
    if (severity >= _EVENT_LOG_ERR)
        tr_logAddError ("%s", message);
    else
        tr_logAddDebug ("%s", message);
}
Beispiel #4
0
static void
onFileAdded (tr_session * session, const char * dir, const char * file)
{
    char * filename = tr_buildPath (dir, file, NULL);
    tr_ctor * ctor = tr_ctorNew (session);
    int err = tr_ctorSetMetainfoFromFile (ctor, filename);

    if (!err)
    {
        tr_torrentNew (ctor, &err, NULL);

        if (err == TR_PARSE_ERR)
            tr_logAddError ("Error parsing .torrent file \"%s\"", file);
        else
        {
            bool trash = false;
            int test = tr_ctorGetDeleteSource (ctor, &trash);

            tr_logAddInfo ("Parsing .torrent file successful \"%s\"", file);

            if (!test && trash)
            {
                tr_logAddInfo ("Deleting input .torrent file \"%s\"", file);
                if (tr_remove (filename))
                    tr_logAddError ("Error deleting .torrent file: %s", tr_strerror (errno));
            }
            else
            {
                char * new_filename = tr_strdup_printf ("%s.added", filename);
                tr_rename (filename, new_filename);
                tr_free (new_filename);
            }
        }
    }

    tr_ctorFree (ctor);
    tr_free (filename);
}
static void
blocklistLoad (tr_blocklistFile * b)
{
  int fd;
  size_t byteCount;
  struct stat st;
  char * base;
  const char * err_fmt = _("Couldn't read \"%1$s\": %2$s");

  blocklistClose (b);

  if (stat (b->filename, &st) == -1)
    return;

  fd = open (b->filename, O_RDONLY | O_BINARY);
  if (fd == -1)
    {
      tr_logAddError (err_fmt, b->filename, tr_strerror (errno));
      return;
    }

  byteCount = (size_t) st.st_size;
  b->rules = mmap (NULL, byteCount, PROT_READ, MAP_PRIVATE, fd, 0);
  if (!b->rules)
    {
      tr_logAddError (err_fmt, b->filename, tr_strerror (errno));
      close (fd);
      return;
    }

  b->fd = fd;
  b->byteCount = byteCount;
  b->ruleCount = byteCount / sizeof (struct tr_ipv4_range);

  base = tr_basename (b->filename);
  tr_logAddInfo (_("Blocklist \"%s\" contains %zu entries"), base, b->ruleCount);
  tr_free (base);
}
Beispiel #6
0
static void
gotsig (int sig)
{
    switch (sig)
    {
        case SIGHUP:
        {
            if (!mySession)
            {
                tr_logAddInfo ("Deferring reload until session is fully started.");
                seenHUP = true;
            }
            else
            {
                tr_variant settings;
                const char * configDir;

                /* reopen the logfile to allow for log rotation */
                if (logfileName) {
                    logfile = freopen (logfileName, LOGFILE_MODE_STR, logfile);
                    if (!logfile)
                        fprintf (stderr, "Couldn't reopen \"%s\": %s\n", logfileName, tr_strerror (errno));
                }

                configDir = tr_sessionGetConfigDir (mySession);
                tr_logAddInfo ("Reloading settings from \"%s\"", configDir);
                tr_variantInitDict (&settings, 0);
                tr_variantDictAddBool (&settings, TR_KEY_rpc_enabled, true);
                tr_sessionLoadSettings (&settings, configDir, MY_NAME);
                tr_sessionSet (mySession, &settings);
                tr_variantFree (&settings);
                tr_sessionReloadBlocklists (mySession);
            }
            break;
        }

        default:
            tr_logAddError ("Unexpected signal (%d) in daemon, closing.", sig);
            /* no break */

        case SIGINT:
        case SIGTERM:
            event_base_loopexit(ev_base, NULL);
            break;
    }
}
void
tr_eventInit (tr_session * session)
{
    tr_event_handle * eh;

    session->events = NULL;

    eh = tr_new0 (tr_event_handle, 1);
    eh->lock = tr_lockNew ();
    if (pipe (eh->fds) == -1)
      tr_logAddError ("Unable to write to pipe() in libtransmission: %s", tr_strerror(errno));
    eh->session = session;
    eh->thread = tr_threadNew (libeventThreadFunc, eh);

    /* wait until the libevent thread is running */
    while (session->events == NULL)
        tr_wait_msec (100);
}
Beispiel #8
0
static void
watchdir_new_impl (dtr_watchdir * w)
{
    int i;
    DIR * odir;
    w->inotify_fd = inotify_init ();

    if (w->inotify_fd < 0)
    {
        i = -1;
    }
    else
    {
        tr_logAddInfo ("Using inotify to watch directory \"%s\"", w->dir);
        i = inotify_add_watch (w->inotify_fd, w->dir, DTR_INOTIFY_MASK);
    }

    if (i < 0)
    {
        tr_logAddError ("Unable to watch \"%s\": %s", w->dir, tr_strerror (errno));
    }
    else if ((odir = opendir (w->dir)))
    {
        struct dirent * d;

        while ((d = readdir (odir)))
        {
            const char * name = d->d_name;

            if (!tr_str_has_suffix (name, ".torrent")) /* skip non-torrents */
                continue;

            tr_logAddInfo ("Found new .torrent file \"%s\" in watchdir \"%s\"", name, w->dir);
            w->callback (w->session, w->dir, name);
        }

        closedir (odir);
    }

}
Beispiel #9
0
static void
watchdir_new_impl (dtr_watchdir * w)
{
    int i;
    tr_sys_dir_t odir;
    w->inotify_fd = inotify_init ();

    if (w->inotify_fd < 0)
    {
        i = -1;
    }
    else
    {
        tr_logAddInfo ("Using inotify to watch directory \"%s\"", w->dir);
        i = inotify_add_watch (w->inotify_fd, w->dir, DTR_INOTIFY_MASK);
    }

    if (i < 0)
    {
        tr_logAddError ("Unable to watch \"%s\": %s", w->dir, tr_strerror (errno));
    }
    else if ((odir = tr_sys_dir_open (w->dir, NULL)) != TR_BAD_SYS_DIR)
    {
        const char * name;
        while ((name = tr_sys_dir_read_name (odir, NULL)) != NULL)
        {
            if (!tr_str_has_suffix (name, ".torrent")) /* skip non-torrents */
                continue;

            tr_logAddInfo ("Found new .torrent file \"%s\" in watchdir \"%s\"", name, w->dir);
            w->callback (w->session, w->dir, name);
        }

        tr_sys_dir_close (odir, NULL);
    }

}
void
tr_runInEventThread (tr_session * session,
                     void func (void*), void * user_data)
{
  assert (tr_isSession (session));
  assert (session->events != NULL);

  if (tr_amInThread (session->events->thread))
    {
      (func)(user_data);
    }
  else
    {
      int fd;
      char ch;
      ssize_t res_1;
      ssize_t res_2;
      tr_event_handle * e = session->events;
      struct tr_run_data data;

      tr_lockLock (e->lock);

      fd = e->fds[1];
      ch = 'r';
      res_1 = pipewrite (fd, &ch, 1);

      data.func = func;
      data.user_data = user_data;
      res_2 = pipewrite (fd, &data, sizeof (data));

      tr_lockUnlock (e->lock);

      if ((res_1 == -1) || (res_2 == -1))
        tr_logAddError ("Unable to write to libtransmisison event queue: %s", tr_strerror(errno));
    }
}
Beispiel #11
0
static tr_socket_t tr_netBindTCPImpl(tr_address const* addr, tr_port port, bool suppressMsgs, int* errOut)
{
    TR_ASSERT(tr_address_is_valid(addr));

    static int const domains[NUM_TR_AF_INET_TYPES] = { AF_INET, AF_INET6 };
    struct sockaddr_storage sock;
    tr_socket_t fd;
    int addrlen;
    int optval;

    fd = socket(domains[addr->type], SOCK_STREAM, 0);

    if (fd == TR_BAD_SOCKET)
    {
        *errOut = sockerrno;
        return TR_BAD_SOCKET;
    }

    if (evutil_make_socket_nonblocking(fd) == -1)
    {
        *errOut = sockerrno;
        tr_netCloseSocket(fd);
        return TR_BAD_SOCKET;
    }

    optval = 1;
    setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void const*)&optval, sizeof(optval));
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void const*)&optval, sizeof(optval));

#ifdef IPV6_V6ONLY

    if (addr->type == TR_AF_INET6)
    {
        if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void const*)&optval, sizeof(optval)) == -1)
        {
            if (sockerrno != ENOPROTOOPT) /* if the kernel doesn't support it, ignore it */
            {
                *errOut = sockerrno;
                tr_netCloseSocket(fd);
                return TR_BAD_SOCKET;
            }
        }
    }

#endif

    addrlen = setup_sockaddr(addr, htons(port), &sock);

    if (bind(fd, (struct sockaddr*)&sock, addrlen) == -1)
    {
        int const err = sockerrno;

        if (!suppressMsgs)
        {
            char const* fmt;
            char const* hint;
            char err_buf[512];

            if (err == EADDRINUSE)
            {
                hint = _("Is another copy of Transmission already running?");
            }
            else
            {
                hint = NULL;
            }

            if (hint == NULL)
            {
                fmt = _("Couldn't bind port %d on %s: %s");
            }
            else
            {
                fmt = _("Couldn't bind port %d on %s: %s (%s)");
            }

            tr_logAddError(fmt, port, tr_address_to_string(addr), tr_net_strerror(err_buf, sizeof(err_buf), err), hint);
        }

        tr_netCloseSocket(fd);
        *errOut = err;
        return TR_BAD_SOCKET;
    }

    if (!suppressMsgs)
    {
        tr_logAddDebug("Bound socket %" PRIdMAX " to port %d on %s", (intmax_t)fd, port, tr_address_to_string(addr));
    }

#ifdef TCP_FASTOPEN

#ifndef SOL_TCP
#define SOL_TCP IPPROTO_TCP
#endif

    optval = 5;
    setsockopt(fd, SOL_TCP, TCP_FASTOPEN, (void const*)&optval, sizeof(optval));

#endif

    if (listen(fd, 128) == -1)
    {
        *errOut = sockerrno;
        tr_netCloseSocket(fd);
        return TR_BAD_SOCKET;
    }

    return fd;
}
Beispiel #12
0
struct tr_peer_socket tr_netOpenPeerSocket(tr_session* session, tr_address const* addr, tr_port port, bool clientIsSeed)
{
    TR_ASSERT(tr_address_is_valid(addr));

    struct tr_peer_socket ret = TR_PEER_SOCKET_INIT;

    static int const domains[NUM_TR_AF_INET_TYPES] = { AF_INET, AF_INET6 };
    tr_socket_t s;
    struct sockaddr_storage sock;
    socklen_t addrlen;
    tr_address const* source_addr;
    socklen_t sourcelen;
    struct sockaddr_storage source_sock;
    char err_buf[512];

    if (!tr_address_is_valid_for_peers(addr, port))
    {
        return ret;
    }

    s = tr_fdSocketCreate(session, domains[addr->type], SOCK_STREAM);

    if (s == TR_BAD_SOCKET)
    {
        return ret;
    }

    /* seeds don't need much of a read buffer... */
    if (clientIsSeed)
    {
        int n = 8192;

        if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void const*)&n, sizeof(n)) == -1)
        {
            tr_logAddInfo("Unable to set SO_RCVBUF on socket %" PRIdMAX ": %s", (intmax_t)s,
                tr_net_strerror(err_buf, sizeof(err_buf), sockerrno));
        }
    }

    if (evutil_make_socket_nonblocking(s) == -1)
    {
        tr_netClose(session, s);
        return ret;
    }

    addrlen = setup_sockaddr(addr, port, &sock);

    /* set source address */
    source_addr = tr_sessionGetPublicAddress(session, addr->type, NULL);
    TR_ASSERT(source_addr != NULL);
    sourcelen = setup_sockaddr(source_addr, 0, &source_sock);

    if (bind(s, (struct sockaddr*)&source_sock, sourcelen) == -1)
    {
        tr_logAddError(_("Couldn't set source address %s on %" PRIdMAX ": %s"), tr_address_to_string(source_addr), (intmax_t)s,
            tr_net_strerror(err_buf, sizeof(err_buf), sockerrno));
        tr_netClose(session, s);
        return ret;
    }

    if (connect(s, (struct sockaddr*)&sock, addrlen) == -1 &&
#ifdef _WIN32
        sockerrno != WSAEWOULDBLOCK &&
#endif
        sockerrno != EINPROGRESS)
    {
        int const tmperrno = sockerrno;

        if ((tmperrno != ENETUNREACH && tmperrno != EHOSTUNREACH) || addr->type == TR_AF_INET)
        {
            tr_logAddError(_("Couldn't connect socket %" PRIdMAX " to %s, port %d (errno %d - %s)"), (intmax_t)s,
                tr_address_to_string(addr), (int)ntohs(port), tmperrno, tr_net_strerror(err_buf, sizeof(err_buf), tmperrno));
        }

        tr_netClose(session, s);
    }
    else
    {
        ret = tr_peer_socket_tcp_create(s);
    }

    tr_logAddDeep(__FILE__, __LINE__, NULL, "New OUTGOING connection %" PRIdMAX " (%s)", (intmax_t)s,
        tr_peerIoAddrStr(addr, port));

    return ret;
}
Beispiel #13
0
int
tr_blocklistFileSetContent (tr_blocklistFile * b, const char * filename)
{
  tr_sys_file_t in;
  tr_sys_file_t out;
  int inCount = 0;
  char line[2048];
  const char * err_fmt = _("Couldn't read \"%1$s\": %2$s");
  struct tr_ipv4_range * ranges = NULL;
  size_t ranges_alloc = 0;
  size_t ranges_count = 0;
  tr_error * error = NULL;

  if (!filename)
    {
      blocklistDelete (b);
      return 0;
    }

  in = tr_sys_file_open (filename, TR_SYS_FILE_READ, 0, &error);
  if (in == TR_BAD_SYS_FILE)
    {
      tr_logAddError (err_fmt, filename, error->message);
      tr_error_free (error);
      return 0;
    }

  blocklistClose (b);

  out = tr_sys_file_open (b->filename,
                          TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE | TR_SYS_FILE_TRUNCATE,
                          0666, &error);
  if (out == TR_BAD_SYS_FILE)
    {
      tr_logAddError (err_fmt, b->filename, error->message);
      tr_error_free (error);
      tr_sys_file_close (in, NULL);
      return 0;
    }

  /* load the rules into memory */
  while (tr_sys_file_read_line (in, line, sizeof (line), NULL))
    {
      struct tr_ipv4_range range;

      ++inCount;

      if (!parseLine (line, &range))
        {
          /* don't try to display the actual lines - it causes issues */
          tr_logAddError (_("blocklist skipped invalid address at line %d"), inCount);
          continue;
        }

      if (ranges_alloc == ranges_count)
        {
          ranges_alloc += 4096; /* arbitrary */
          ranges = tr_renew (struct tr_ipv4_range, ranges, ranges_alloc);
        }

      ranges[ranges_count++] = range;
    }
Beispiel #14
0
/**
 * returns 0 on success, or an errno value on failure.
 * errno values include ENOENT if the parent folder doesn't exist,
 * plus the errno values set by tr_sys_dir_create () and tr_sys_file_open ().
 */
static int
cached_file_open (struct tr_cached_file  * o,
                  const char             * filename,
                  bool                     writable,
                  tr_preallocation_mode    allocation,
                  uint64_t                 file_size)
{
  int flags;
  tr_sys_path_info info;
  bool already_existed;
  bool resize_needed;
  tr_sys_file_t fd = TR_BAD_SYS_FILE;
  tr_error * error = NULL;

  /* create subfolders, if any */
  if (writable)
    {
      char * dir = tr_sys_path_dirname (filename, NULL);
      if (!tr_sys_dir_create (dir, TR_SYS_DIR_CREATE_PARENTS, 0777, &error))
        {
          tr_logAddError (_("Couldn't create \"%1$s\": %2$s"), dir, error->message);
          tr_free (dir);
          goto fail;
        }
      tr_free (dir);
    }

  already_existed = tr_sys_path_get_info (filename, 0, &info, NULL) && info.type == TR_SYS_PATH_IS_FILE;

  /* we can't resize the file w/o write permissions */
  resize_needed = already_existed && (file_size < info.size);
  writable |= resize_needed;

  /* open the file */
  flags = writable ? (TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE) : 0;
  flags |= TR_SYS_FILE_READ | TR_SYS_FILE_SEQUENTIAL;
  fd = tr_sys_file_open (filename, flags, 0666, &error);

  if (fd == TR_BAD_SYS_FILE)
    {
      tr_logAddError (_("Couldn't open \"%1$s\": %2$s"), filename, error->message);
      goto fail;
    }

  if (writable && !already_existed && allocation != TR_PREALLOCATE_NONE)
    {
      bool success = false;
      const char * type = NULL;

      if (allocation == TR_PREALLOCATE_FULL)
        {
          success = preallocate_file_full (fd, file_size, &error);
          type = _("full");
        }
      else if (allocation == TR_PREALLOCATE_SPARSE)
        {
          success = preallocate_file_sparse (fd, file_size, &error);
          type = _("sparse");
        }

      assert (type != NULL);

      if (!success)
        {
          tr_logAddError (_("Couldn't preallocate file \"%1$s\" (%2$s, size: %3$"PRIu64"): %4$s"),
            filename, type, file_size, error->message);
          goto fail;
        }

      tr_logAddDebug (_("Preallocated file \"%1$s\" (%2$s, size: %3$"PRIu64")"), filename, type, file_size);
    }

  /* If the file already exists and it's too large, truncate it.
   * This is a fringe case that happens if a torrent's been updated
   * and one of the updated torrent's files is smaller.
   * http://trac.transmissionbt.com/ticket/2228
   * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249
   */
  if (resize_needed && !tr_sys_file_truncate (fd, file_size, &error))
    {
      tr_logAddError (_("Couldn't truncate \"%1$s\": %2$s"), filename, error->message);
      goto fail;
    }

  o->fd = fd;
  return 0;

fail:
  {
    const int err = error->code;
    tr_error_free (error);

    if (fd != TR_BAD_SYS_FILE)
      tr_sys_file_close (fd, NULL);

    return err;
  }
}
Beispiel #15
0
/**
 * returns 0 on success, or an errno value on failure.
 * errno values include ENOENT if the parent folder doesn't exist,
 * plus the errno values set by tr_mkdirp () and open ().
 */
static int
cached_file_open (struct tr_cached_file  * o,
                  const char             * filename,
                  bool                     writable,
                  tr_preallocation_mode    allocation,
                  uint64_t                 file_size)
{
  int flags;
  struct stat sb;
  bool already_existed;
  bool resize_needed;

  /* create subfolders, if any */
  if (writable)
    {
      char * dir = tr_dirname (filename);
      const int err = tr_mkdirp (dir, 0777) ? errno : 0;
      if (err)
        {
          tr_logAddError (_("Couldn't create \"%1$s\": %2$s"), dir, tr_strerror (err));
          tr_free (dir);
          return err;
        }
      tr_free (dir);
    }

  already_existed = !stat (filename, &sb) && S_ISREG (sb.st_mode);

  if (writable && !already_existed && (allocation == TR_PREALLOCATE_FULL))
    if (preallocate_file_full (filename, file_size))
      tr_logAddDebug ("Preallocated file \"%s\"", filename);

  /* we can't resize the file w/o write permissions */
  resize_needed = already_existed && (file_size < (uint64_t)sb.st_size);
  writable |= resize_needed;

  /* open the file */
  flags = writable ? (O_RDWR | O_CREAT) : O_RDONLY;
  flags |= O_LARGEFILE | O_BINARY | O_SEQUENTIAL;
  o->fd = open (filename, flags, 0666);

  if (o->fd == -1)
    {
      const int err = errno;
      tr_logAddError (_("Couldn't open \"%1$s\": %2$s"), filename, tr_strerror (err));
      return err;
    }

  /* If the file already exists and it's too large, truncate it.
   * This is a fringe case that happens if a torrent's been updated
   * and one of the updated torrent's files is smaller.
   * http://trac.transmissionbt.com/ticket/2228
   * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249
   */
  if (resize_needed && (ftruncate (o->fd, file_size) == -1))
    {
      const int err = errno;
      tr_logAddError (_("Couldn't truncate \"%1$s\": %2$s"), filename, tr_strerror (err));
      return err;
    }

  if (writable && !already_existed && (allocation == TR_PREALLOCATE_SPARSE))
    preallocate_file_sparse (o->fd, file_size);

  /* Many (most?) clients request blocks in ascending order,
   * so increase the readahead buffer.
   * Also, disable OS-level caching because "inactive memory" angers users. */
  tr_set_file_for_single_pass (o->fd);

  return 0;
}
Beispiel #16
0
static bool
create_path (const char  * path_in,
             int           permissions,
             tr_error   ** error)
{
  char * p;
  char * pp;
  bool done;
  int tmperr;
  int rv;
  struct stat sb;
  char * path;

  /* make a temporary copy of path */
  path = tr_strdup (path_in);

  /* walk past the root */
  p = path;
  while (*p == TR_PATH_DELIMITER)
    ++p;

  pp = p;
  done = false;
  while ((p = strchr (pp, TR_PATH_DELIMITER)) || (p = strchr (pp, '\0')))
    {
      if (!*p)
        done = true;
      else
        *p = '\0';

      tmperr = errno;
      rv = stat (path, &sb);
      errno = tmperr;
      if (rv)
        {
          tr_error * my_error = NULL;

          /* Folder doesn't exist yet */
          if (!tr_sys_dir_create (path, 0, permissions, &my_error))
            {
              tr_logAddError (_ ("Couldn't create \"%1$s\": %2$s"), path, my_error->message);
              tr_free (path);
              tr_error_propagate (error, &my_error);
              return false;
            }
        }
      else if ((sb.st_mode & S_IFMT) != S_IFDIR)
        {
          /* Node exists but isn't a folder */
          char * const buf = tr_strdup_printf (_ ("File \"%s\" is in the way"), path);
          tr_logAddError (_ ("Couldn't create \"%1$s\": %2$s"), path_in, buf);
          tr_free (buf);
          tr_free (path);
          set_system_error (error, ENOTDIR);
          return false;
        }

      if (done)
        break;

      *p = TR_PATH_DELIMITER;
      p++;
      pp = p;
    }

  tr_free (path);
  return true;
}
int
tr_blocklistFileSetContent (tr_blocklistFile * b, const char * filename)
{
  FILE * in;
  FILE * out;
  int inCount = 0;
  char line[2048];
  const char * err_fmt = _("Couldn't read \"%1$s\": %2$s");
  struct tr_ipv4_range * ranges = NULL;
  size_t ranges_alloc = 0;
  size_t ranges_count = 0;

  if (!filename)
    {
      blocklistDelete (b);
      return 0;
    }

  in = fopen (filename, "rb");
  if (in == NULL)
    {
      tr_logAddError (err_fmt, filename, tr_strerror (errno));
      return 0;
    }

  blocklistClose (b);

  out = fopen (b->filename, "wb+");
  if (out == NULL)
    {
      tr_logAddError (err_fmt, b->filename, tr_strerror (errno));
      fclose (in);
      return 0;
    }

  /* load the rules into memory */
  while (fgets (line, sizeof (line), in) != NULL)
    {
      char * walk;
      struct tr_ipv4_range range;

      ++inCount;

      /* zap the linefeed */
      if ((walk = strchr (line, '\r'))) *walk = '\0';
      if ((walk = strchr (line, '\n'))) *walk = '\0';

      if (!parseLine (line, &range))
        {
          /* don't try to display the actual lines - it causes issues */
          tr_logAddError (_("blocklist skipped invalid address at line %d"), inCount);
          continue;
        }

      if (ranges_alloc == ranges_count)
        {
          ranges_alloc += 4096; /* arbitrary */
          ranges = tr_renew (struct tr_ipv4_range, ranges, ranges_alloc);
        }

      ranges[ranges_count++] = range;
    }