static int fs_format(int cidx) {
  s32_t r;
  struct mount_info *m = &s_fsm;
  _u32 fsc_size = FS_CONTAINER_SIZE(FS_SIZE);
  dprintf(("formatting %d s=%u, cs=%d\n", cidx, FS_SIZE, (int) fsc_size));

  m->cidx = cidx;
  r = fs_create_container(cidx, FS_SIZE);
  if (r < 0) goto out;
  m->fh = r;
  m->valid = m->rw = m->formatting = 1;
  /* Touch a byte at the end to open a "hole". */
  r = sl_FsWrite(m->fh, fsc_size - 1, (_u8 *) "\xff", 1);
  dprintf(("write 1 @ %d %d\n", (int) (fsc_size - 1), (int) r));
  if (r != 1) goto out_close;

  /* There must be a mount attempt before format. It'll fail and that's ok. */
  r = fs_mount_spiffs(m, FS_SIZE, FS_BLOCK_SIZE, FS_PAGE_SIZE);
  dprintf(("mount: %d\n", (int) r));
  r = SPIFFS_format(&m->fs);
  dprintf(("format: %d\n", (int) r));
  if (r != SPIFFS_OK) goto out_close;

  m->seq = INITIAL_SEQ;
  r = fs_write_meta(m);

out_close:
  sl_FsClose(m->fh, NULL, NULL, 0);
out:
  m->fh = -1;
  m->valid = m->formatting = m->rw = 0;
  return r;
}
static _i32 fs_switch_container(struct mount_info *m, _u32 mask_begin,
                                _u32 mask_len) {
  int r;
  int new_cidx = m->cidx ^ 1;
  _i32 old_fh = m->fh, new_fh;
  _u8 *buf;
  _u32 offset, len, buf_size;
  LOG(LL_DEBUG, ("%s %d -> %d", m->cpfx, m->cidx, new_cidx));
  if (old_fh > 0 && m->rw) {
    /*
     * During the switch the destination container will be unusable.
     * If switching from a writeable container (likely in response to an erase),
     * close the old container first to make it safe and reopen for reading.
     */
    fs_close_container(m);
    old_fh = -1;
  }
  if (old_fh < 0) {
    _u8 fname[MAX_FS_CONTAINER_FNAME_LEN];
    fs_container_fname(m->cpfx, m->cidx, fname);
    r = sl_FsOpen(fname, FS_MODE_OPEN_READ, NULL, &old_fh);
    DBG(("fopen %s %d", m->cpfx, r));
    if (r < 0) {
      r = SPIFFS_ERR_NOT_READABLE;
      goto out_close_old;
    }
  }
  miot_wdt_feed();
  new_fh = fs_create_container(m->cpfx, new_cidx, m->fs.cfg.phys_size);
  if (new_fh < 0) {
    r = new_fh;
    goto out_close_old;
  }

  buf_size = 1024;
  buf = get_buf(&buf_size);
  if (buf == NULL) {
    r = SPIFFS_ERR_INTERNAL;
    goto out_close_new;
  }

  for (offset = 0; offset < m->fs.cfg.phys_size;) {
    len = buf_size;
    if (offset == mask_begin) {
      offset = mask_begin + mask_len;
    } else if (offset + len > mask_begin && offset < mask_begin + mask_len) {
      len = mask_begin - offset;
    }
    if (offset + len > m->fs.cfg.phys_size) {
      len = m->fs.cfg.phys_size - offset;
    }
    DBG(("copy %d @ %d", (int) len, (int) offset));
    if (len > 0) {
      r = sl_FsRead(old_fh, offset, buf, len);
      if (r != len) {
        r = SPIFFS_ERR_NOT_READABLE;
        goto out_free;
      }
      r = sl_FsWrite(new_fh, offset, buf, len);
      if (r != len) {
        r = SPIFFS_ERR_NOT_WRITABLE;
        goto out_free;
      }
      offset += len;
    }
  }

  m->seq--;
  m->cidx = new_cidx;
  m->fh = new_fh;
  new_fh = -1;
  m->rw = 1;

  r = fs_write_mount_meta(m);

  m->last_write = mg_time();

out_free:
  free(buf);
out_close_new:
  if (new_fh > 0) sl_FsClose(new_fh, NULL, NULL, 0);
out_close_old:
  sl_FsClose(old_fh, NULL, NULL, 0);
  LOG((r == 0 ? LL_DEBUG : LL_ERROR), ("%d", r));
  return r;
}
static _i32 fs_switch_container(struct mount_info *m, _u32 mask_begin,
                                _u32 mask_len) {
  int r;
  int new_cidx = m->cidx ^ 1;
  _i32 old_fh = m->fh, new_fh;
  _u8 *buf;
  _u32 offset, len, buf_size;
  dprintf(("switch %d -> %d\n", m->cidx, new_cidx));
  if (old_fh < 0) {
    r = sl_FsOpen(container_fname(m->cidx), FS_MODE_OPEN_READ, NULL, &old_fh);
    dprintf(("fopen %d\n", r));
    if (r < 0) {
      r = SPIFFS_ERR_NOT_READABLE;
      goto out_close_old;
    }
  }
  new_fh = fs_create_container(new_cidx, m->fs.cfg.phys_size);
  if (new_fh < 0) {
    r = new_fh;
    goto out_close_old;
  }

  buf_size = 8192;
  buf = get_buf(&buf_size);
  if (buf == NULL) {
    r = SPIFFS_ERR_INTERNAL;
    goto out_close_new;
  }

  for (offset = 0; offset < m->fs.cfg.phys_size;) {
    len = buf_size;
    if (offset == mask_begin) {
      offset = mask_begin + mask_len;
    } else if (offset + len > mask_begin && offset < mask_begin + mask_len) {
      len = mask_begin - offset;
    }
    if (offset + len > m->fs.cfg.phys_size) {
      len = m->fs.cfg.phys_size - offset;
    }
    dprintf(("copy %d @ %d\n", (int) len, (int) offset));
    if (len > 0) {
      r = sl_FsRead(old_fh, offset, buf, len);
      if (r != len) {
        r = SPIFFS_ERR_NOT_READABLE;
        goto out_free;
      }
      r = sl_FsWrite(new_fh, offset, buf, len);
      if (r != len) {
        r = SPIFFS_ERR_NOT_WRITABLE;
        goto out_free;
      }
      offset += len;
    }
  }

  m->seq--;
  m->cidx = new_cidx;
  m->fh = new_fh;
  new_fh = -1;
  m->rw = 1;

  r = fs_write_meta(m);

out_free:
  free(buf);
out_close_new:
  if (new_fh > 0) sl_FsClose(new_fh, NULL, NULL, 0);
out_close_old:
  sl_FsClose(old_fh, NULL, NULL, 0);
  dprintf(("switch: %d\n", r));
  return r;
}