|
|
|
@@ -157,6 +157,7 @@ struct sshfs {
|
|
|
|
|
char *workarounds;
|
|
|
|
|
int rename_workaround;
|
|
|
|
|
int nodelay_workaround;
|
|
|
|
|
int truncate_workaround;
|
|
|
|
|
int transform_symlinks;
|
|
|
|
|
int detect_uid;
|
|
|
|
|
unsigned max_read;
|
|
|
|
@@ -266,12 +267,16 @@ static struct fuse_opt sshfs_opts[] = {
|
|
|
|
|
static struct fuse_opt workaround_opts[] = {
|
|
|
|
|
SSHFS_OPT("none", rename_workaround, 0),
|
|
|
|
|
SSHFS_OPT("none", nodelay_workaround, 0),
|
|
|
|
|
SSHFS_OPT("none", truncate_workaround, 0),
|
|
|
|
|
SSHFS_OPT("all", rename_workaround, 1),
|
|
|
|
|
SSHFS_OPT("all", nodelay_workaround, 1),
|
|
|
|
|
SSHFS_OPT("all", truncate_workaround, 1),
|
|
|
|
|
SSHFS_OPT("rename", rename_workaround, 1),
|
|
|
|
|
SSHFS_OPT("norename", rename_workaround, 0),
|
|
|
|
|
SSHFS_OPT("nodelay", nodelay_workaround, 1),
|
|
|
|
|
SSHFS_OPT("nonodelay", nodelay_workaround, 0),
|
|
|
|
|
SSHFS_OPT("truncate", truncate_workaround, 1),
|
|
|
|
|
SSHFS_OPT("notruncate", truncate_workaround, 0),
|
|
|
|
|
FUSE_OPT_END
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@@ -791,26 +796,45 @@ static uint32_t sftp_get_id(void)
|
|
|
|
|
return idctr++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void buf_to_iov(struct buffer *buf, struct iovec *iov)
|
|
|
|
|
static void buf_to_iov(const struct buffer *buf, struct iovec *iov)
|
|
|
|
|
{
|
|
|
|
|
iov->iov_base = buf->p;
|
|
|
|
|
iov->iov_len = buf->len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int sftp_send(uint8_t type, struct buffer *buf)
|
|
|
|
|
static size_t iov_length(const struct iovec *iov, unsigned long nr_segs)
|
|
|
|
|
{
|
|
|
|
|
unsigned long seg;
|
|
|
|
|
size_t ret = 0;
|
|
|
|
|
|
|
|
|
|
for (seg = 0; seg < nr_segs; seg++)
|
|
|
|
|
ret += iov[seg].iov_len;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define SFTP_MAX_IOV 3
|
|
|
|
|
|
|
|
|
|
static int sftp_send_iov(uint8_t type, uint32_t id, struct iovec iov[],
|
|
|
|
|
size_t count)
|
|
|
|
|
{
|
|
|
|
|
int res;
|
|
|
|
|
struct buffer buf2;
|
|
|
|
|
struct iovec iov[2];
|
|
|
|
|
buf_init(&buf2, 5);
|
|
|
|
|
buf_add_uint32(&buf2, buf->len + 1);
|
|
|
|
|
buf_add_uint8(&buf2, type);
|
|
|
|
|
buf_to_iov(&buf2, &iov[0]);
|
|
|
|
|
buf_to_iov(buf, &iov[1]);
|
|
|
|
|
struct buffer buf;
|
|
|
|
|
struct iovec iovout[SFTP_MAX_IOV];
|
|
|
|
|
unsigned i;
|
|
|
|
|
unsigned nout = 0;
|
|
|
|
|
|
|
|
|
|
assert(count <= SFTP_MAX_IOV - 1);
|
|
|
|
|
buf_init(&buf, 5);
|
|
|
|
|
buf_add_uint32(&buf, iov_length(iov, count) + 5);
|
|
|
|
|
buf_add_uint8(&buf, type);
|
|
|
|
|
buf_add_uint32(&buf, id);
|
|
|
|
|
buf_to_iov(&buf, &iovout[nout++]);
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
iovout[nout++] = iov[i];
|
|
|
|
|
pthread_mutex_lock(&sshfs.lock_write);
|
|
|
|
|
res = do_write(iov, 2);
|
|
|
|
|
res = do_write(iovout, nout);
|
|
|
|
|
pthread_mutex_unlock(&sshfs.lock_write);
|
|
|
|
|
buf_free(&buf2);
|
|
|
|
|
buf_free(&buf);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -975,11 +999,9 @@ static int sftp_init()
|
|
|
|
|
uint8_t type;
|
|
|
|
|
uint32_t version;
|
|
|
|
|
struct buffer buf;
|
|
|
|
|
buf_init(&buf, 4);
|
|
|
|
|
buf_add_uint32(&buf, PROTO_VERSION);
|
|
|
|
|
if (sftp_send(SSH_FXP_INIT, &buf) == -1)
|
|
|
|
|
buf_init(&buf, 0);
|
|
|
|
|
if (sftp_send_iov(SSH_FXP_INIT, PROTO_VERSION, NULL, 0) == -1)
|
|
|
|
|
goto out;
|
|
|
|
|
buf_clear(&buf);
|
|
|
|
|
if (sftp_read(&type, &buf) == -1)
|
|
|
|
|
goto out;
|
|
|
|
|
if (type != SSH_FXP_VERSION) {
|
|
|
|
@@ -1009,11 +1031,12 @@ static void sftp_detect_uid()
|
|
|
|
|
uint8_t type;
|
|
|
|
|
struct buffer buf;
|
|
|
|
|
struct stat stbuf;
|
|
|
|
|
struct iovec iov[1];
|
|
|
|
|
|
|
|
|
|
buf_init(&buf, 9);
|
|
|
|
|
buf_add_uint32(&buf, id);
|
|
|
|
|
buf_init(&buf, 5);
|
|
|
|
|
buf_add_string(&buf, ".");
|
|
|
|
|
if (sftp_send(SSH_FXP_STAT, &buf) == -1)
|
|
|
|
|
buf_to_iov(&buf, &iov[0]);
|
|
|
|
|
if (sftp_send_iov(SSH_FXP_STAT, id, iov, 1) == -1)
|
|
|
|
|
goto out;
|
|
|
|
|
buf_clear(&buf);
|
|
|
|
|
if (sftp_read(&type, &buf) == -1)
|
|
|
|
@@ -1183,13 +1206,12 @@ static int sftp_request_wait(struct request *req, uint8_t type,
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int sftp_request_send(uint8_t type, const struct buffer *buf,
|
|
|
|
|
static int sftp_request_send(uint8_t type, struct iovec *iov, size_t count,
|
|
|
|
|
request_func begin_func, request_func end_func,
|
|
|
|
|
int want_reply, void *data,
|
|
|
|
|
struct request **reqp)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
struct buffer buf2;
|
|
|
|
|
uint32_t id;
|
|
|
|
|
struct request *req = g_new0(struct request, 1);
|
|
|
|
|
|
|
|
|
@@ -1198,13 +1220,10 @@ static int sftp_request_send(uint8_t type, const struct buffer *buf,
|
|
|
|
|
req->data = data;
|
|
|
|
|
sem_init(&req->ready, 0, 0);
|
|
|
|
|
buf_init(&req->reply, 0);
|
|
|
|
|
buf_init(&buf2, buf->len + 4);
|
|
|
|
|
pthread_mutex_lock(&sshfs.lock);
|
|
|
|
|
if (begin_func)
|
|
|
|
|
begin_func(req);
|
|
|
|
|
id = sftp_get_id();
|
|
|
|
|
buf_add_uint32(&buf2, id);
|
|
|
|
|
buf_add_mem(&buf2, buf->p, buf->len);
|
|
|
|
|
err = start_processing_thread();
|
|
|
|
|
if (err) {
|
|
|
|
|
pthread_mutex_unlock(&sshfs.lock);
|
|
|
|
@@ -1216,37 +1235,33 @@ static int sftp_request_send(uint8_t type, const struct buffer *buf,
|
|
|
|
|
pthread_mutex_unlock(&sshfs.lock);
|
|
|
|
|
|
|
|
|
|
err = -EIO;
|
|
|
|
|
if (sftp_send(type, &buf2) == -1) {
|
|
|
|
|
if (sftp_send_iov(type, id, iov, count) == -1) {
|
|
|
|
|
pthread_mutex_lock(&sshfs.lock);
|
|
|
|
|
g_hash_table_remove(sshfs.reqtab, GUINT_TO_POINTER(id));
|
|
|
|
|
pthread_mutex_unlock(&sshfs.lock);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
buf_free(&buf2);
|
|
|
|
|
if (want_reply)
|
|
|
|
|
*reqp = req;
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
buf_free(&buf2);
|
|
|
|
|
req->error = err;
|
|
|
|
|
if (!want_reply)
|
|
|
|
|
sftp_request_wait(req, type, 0, NULL);
|
|
|
|
|
else
|
|
|
|
|
*reqp = req;
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int sftp_request_common(uint8_t type, const struct buffer *buf,
|
|
|
|
|
uint8_t expect_type, struct buffer *outbuf,
|
|
|
|
|
request_func begin_func, request_func end_func,
|
|
|
|
|
void *data)
|
|
|
|
|
static int sftp_request_iov(uint8_t type, struct iovec *iov, size_t count,
|
|
|
|
|
uint8_t expect_type, struct buffer *outbuf)
|
|
|
|
|
{
|
|
|
|
|
struct request *req;
|
|
|
|
|
|
|
|
|
|
sftp_request_send(type, buf, begin_func, end_func, expect_type, data,
|
|
|
|
|
&req);
|
|
|
|
|
|
|
|
|
|
sftp_request_send(type, iov, count, NULL, NULL, expect_type, NULL, &req);
|
|
|
|
|
if (expect_type == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
@@ -1256,15 +1271,10 @@ static int sftp_request_common(uint8_t type, const struct buffer *buf,
|
|
|
|
|
static int sftp_request(uint8_t type, const struct buffer *buf,
|
|
|
|
|
uint8_t expect_type, struct buffer *outbuf)
|
|
|
|
|
{
|
|
|
|
|
return sftp_request_common(type, buf, expect_type, outbuf, NULL, NULL,
|
|
|
|
|
NULL);
|
|
|
|
|
}
|
|
|
|
|
struct iovec iov;
|
|
|
|
|
|
|
|
|
|
static int sftp_request_async(uint8_t type, const struct buffer *buf,
|
|
|
|
|
request_func begin_func, request_func end_func,
|
|
|
|
|
void *data)
|
|
|
|
|
{
|
|
|
|
|
return sftp_request_send(type, buf, begin_func, end_func, 0, data, NULL);
|
|
|
|
|
buf_to_iov(buf, &iov);
|
|
|
|
|
return sftp_request_iov(type, &iov, 1, expect_type, outbuf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int sshfs_getattr(const char *path, struct stat *stbuf)
|
|
|
|
@@ -1331,7 +1341,7 @@ static void transform_symlink(const char *path, char **linkp)
|
|
|
|
|
return;
|
|
|
|
|
dotdots--;
|
|
|
|
|
|
|
|
|
|
newlink = malloc(dotdots * 3 + l ? strlen(l) : 1 + 10);
|
|
|
|
|
newlink = malloc(dotdots * 3 + strlen(l) + 2);
|
|
|
|
|
if (!newlink) {
|
|
|
|
|
fprintf(stderr, "sshfs: memory allocation failed\n");
|
|
|
|
|
exit(1);
|
|
|
|
@@ -1568,34 +1578,23 @@ static int sshfs_chown(const char *path, uid_t uid, gid_t gid)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int sshfs_truncate_workaround(const char *path, off_t size,
|
|
|
|
|
struct fuse_file_info *fi);
|
|
|
|
|
|
|
|
|
|
static int sshfs_truncate(const char *path, off_t size)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
struct buffer buf;
|
|
|
|
|
|
|
|
|
|
sshfs.modifver ++;
|
|
|
|
|
if (size == 0 || sshfs.truncate_workaround)
|
|
|
|
|
return sshfs_truncate_workaround(path, size, NULL);
|
|
|
|
|
|
|
|
|
|
buf_init(&buf, 0);
|
|
|
|
|
buf_add_path(&buf, path);
|
|
|
|
|
if (size == 0) {
|
|
|
|
|
/* If size is zero, use open(..., O_TRUNC), to work around
|
|
|
|
|
broken sftp servers */
|
|
|
|
|
struct buffer handle;
|
|
|
|
|
buf_add_uint32(&buf, SSH_FXF_WRITE | SSH_FXF_TRUNC);
|
|
|
|
|
buf_add_uint32(&buf, 0);
|
|
|
|
|
err = sftp_request(SSH_FXP_OPEN, &buf, SSH_FXP_HANDLE, &handle);
|
|
|
|
|
if (!err) {
|
|
|
|
|
int err2;
|
|
|
|
|
buf_finish(&handle);
|
|
|
|
|
err2 = sftp_request(SSH_FXP_CLOSE, &handle, 0, NULL);
|
|
|
|
|
if (!err)
|
|
|
|
|
err = err2;
|
|
|
|
|
buf_free(&handle);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
buf_add_uint32(&buf, SSH_FILEXFER_ATTR_SIZE);
|
|
|
|
|
buf_add_uint64(&buf, size);
|
|
|
|
|
err = sftp_request(SSH_FXP_SETSTAT, &buf, SSH_FXP_STATUS, NULL);
|
|
|
|
|
}
|
|
|
|
|
buf_add_uint32(&buf, SSH_FILEXFER_ATTR_SIZE);
|
|
|
|
|
buf_add_uint64(&buf, size);
|
|
|
|
|
err = sftp_request(SSH_FXP_SETSTAT, &buf, SSH_FXP_STATUS, NULL);
|
|
|
|
|
buf_free(&buf);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
@@ -1630,6 +1629,8 @@ static int sshfs_open_common(const char *path, mode_t mode,
|
|
|
|
|
struct sshfs_file *sf;
|
|
|
|
|
struct request *open_req;
|
|
|
|
|
uint32_t pflags = 0;
|
|
|
|
|
struct iovec iov;
|
|
|
|
|
|
|
|
|
|
if ((fi->flags & O_ACCMODE) == O_RDONLY)
|
|
|
|
|
pflags = SSH_FXF_READ;
|
|
|
|
|
else if((fi->flags & O_ACCMODE) == O_WRONLY)
|
|
|
|
@@ -1661,7 +1662,8 @@ static int sshfs_open_common(const char *path, mode_t mode,
|
|
|
|
|
buf_add_uint32(&buf, pflags);
|
|
|
|
|
buf_add_uint32(&buf, SSH_FILEXFER_ATTR_PERMISSIONS);
|
|
|
|
|
buf_add_uint32(&buf, mode);
|
|
|
|
|
sftp_request_send(SSH_FXP_OPEN, &buf, NULL, NULL, 1, NULL, &open_req);
|
|
|
|
|
buf_to_iov(&buf, &iov);
|
|
|
|
|
sftp_request_send(SSH_FXP_OPEN, &iov, 1, NULL, NULL, 1, NULL, &open_req);
|
|
|
|
|
buf_clear(&buf);
|
|
|
|
|
buf_add_path(&buf, path);
|
|
|
|
|
err2 = sftp_request(SSH_FXP_LSTAT, &buf, SSH_FXP_ATTRS, &outbuf);
|
|
|
|
@@ -1823,12 +1825,15 @@ static void sshfs_send_async_read(struct sshfs_file *sf,
|
|
|
|
|
{
|
|
|
|
|
struct buffer buf;
|
|
|
|
|
struct buffer *handle = &sf->handle;
|
|
|
|
|
struct iovec iov;
|
|
|
|
|
|
|
|
|
|
buf_init(&buf, 0);
|
|
|
|
|
buf_add_buf(&buf, handle);
|
|
|
|
|
buf_add_uint64(&buf, chunk->offset);
|
|
|
|
|
buf_add_uint32(&buf, chunk->size);
|
|
|
|
|
sftp_request_async(SSH_FXP_READ, &buf, sshfs_read_begin, sshfs_read_end,
|
|
|
|
|
chunk);
|
|
|
|
|
buf_to_iov(&buf, &iov);
|
|
|
|
|
sftp_request_send(SSH_FXP_READ, &iov, 1, sshfs_read_begin, sshfs_read_end,
|
|
|
|
|
0, chunk, NULL);
|
|
|
|
|
buf_free(&buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -1971,9 +1976,9 @@ static int sshfs_write(const char *path, const char *wbuf, size_t size,
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
struct buffer buf;
|
|
|
|
|
struct buffer data;
|
|
|
|
|
struct sshfs_file *sf = get_sshfs_file(fi);
|
|
|
|
|
struct buffer *handle = &sf->handle;
|
|
|
|
|
struct iovec iov[2];
|
|
|
|
|
|
|
|
|
|
(void) path;
|
|
|
|
|
|
|
|
|
@@ -1981,17 +1986,18 @@ static int sshfs_write(const char *path, const char *wbuf, size_t size,
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
sshfs.modifver ++;
|
|
|
|
|
data.p = (uint8_t *) wbuf;
|
|
|
|
|
data.len = size;
|
|
|
|
|
buf_init(&buf, 0);
|
|
|
|
|
buf_add_buf(&buf, handle);
|
|
|
|
|
buf_add_uint64(&buf, offset);
|
|
|
|
|
buf_add_data(&buf, &data);
|
|
|
|
|
buf_add_uint32(&buf, size);
|
|
|
|
|
buf_to_iov(&buf, &iov[0]);
|
|
|
|
|
iov[1].iov_base = (void *) wbuf;
|
|
|
|
|
iov[1].iov_len = size;
|
|
|
|
|
if (!sshfs.sync_write && !sf->write_error)
|
|
|
|
|
err = sftp_request_async(SSH_FXP_WRITE, &buf, sshfs_write_begin,
|
|
|
|
|
sshfs_write_end, sf);
|
|
|
|
|
err = sftp_request_send(SSH_FXP_WRITE, iov, 2, sshfs_write_begin,
|
|
|
|
|
sshfs_write_end, 0, sf, NULL);
|
|
|
|
|
else
|
|
|
|
|
err = sftp_request(SSH_FXP_WRITE, &buf, SSH_FXP_STATUS, NULL);
|
|
|
|
|
err = sftp_request_iov(SSH_FXP_WRITE, iov, 2, SSH_FXP_STATUS, NULL);
|
|
|
|
|
buf_free(&buf);
|
|
|
|
|
return err ? err : (int) size;
|
|
|
|
|
}
|
|
|
|
@@ -2047,6 +2053,9 @@ static int sshfs_ftruncate(const char *path, off_t size,
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
sshfs.modifver ++;
|
|
|
|
|
if (sshfs.truncate_workaround)
|
|
|
|
|
return sshfs_truncate_workaround(path, size, fi);
|
|
|
|
|
|
|
|
|
|
buf_init(&buf, 0);
|
|
|
|
|
buf_add_buf(&buf, &sf->handle);
|
|
|
|
|
buf_add_uint32(&buf, SSH_FILEXFER_ATTR_SIZE);
|
|
|
|
@@ -2056,6 +2065,7 @@ static int sshfs_ftruncate(const char *path, off_t size,
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static int sshfs_fgetattr(const char *path, struct stat *stbuf,
|
|
|
|
|
struct fuse_file_info *fi)
|
|
|
|
@@ -2081,7 +2091,128 @@ static int sshfs_fgetattr(const char *path, struct stat *stbuf,
|
|
|
|
|
buf_free(&buf);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static int sshfs_truncate_zero(const char *path)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
struct fuse_file_info fi;
|
|
|
|
|
|
|
|
|
|
fi.flags = O_WRONLY | O_TRUNC;
|
|
|
|
|
err = sshfs_open(path, &fi);
|
|
|
|
|
if (!err)
|
|
|
|
|
sshfs_release(path, &fi);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static size_t calc_buf_size(off_t size, off_t offset)
|
|
|
|
|
{
|
|
|
|
|
return offset + sshfs.max_read < size ? sshfs.max_read : size - offset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int sshfs_truncate_shrink(const char *path, off_t size)
|
|
|
|
|
{
|
|
|
|
|
int res;
|
|
|
|
|
char *data;
|
|
|
|
|
off_t offset;
|
|
|
|
|
struct fuse_file_info fi;
|
|
|
|
|
|
|
|
|
|
data = calloc(size, 1);
|
|
|
|
|
if (!data)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
fi.flags = O_RDONLY;
|
|
|
|
|
res = sshfs_open(path, &fi);
|
|
|
|
|
if (res)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
for (offset = 0; offset < size; offset += res) {
|
|
|
|
|
size_t bufsize = calc_buf_size(size, offset);
|
|
|
|
|
res = sshfs_read(path, data + offset, bufsize, offset, &fi);
|
|
|
|
|
if (res <= 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
sshfs_release(path, &fi);
|
|
|
|
|
if (res < 0)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
fi.flags = O_WRONLY | O_TRUNC;
|
|
|
|
|
res = sshfs_open(path, &fi);
|
|
|
|
|
if (res)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
for (offset = 0; offset < size; offset += res) {
|
|
|
|
|
size_t bufsize = calc_buf_size(size, offset);
|
|
|
|
|
res = sshfs_write(path, data + offset, bufsize, offset, &fi);
|
|
|
|
|
if (res < 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (res >= 0)
|
|
|
|
|
res = sshfs_flush(path, &fi);
|
|
|
|
|
sshfs_release(path, &fi);
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
free(data);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int sshfs_truncate_extend(const char *path, off_t size,
|
|
|
|
|
struct fuse_file_info *fi)
|
|
|
|
|
{
|
|
|
|
|
int res;
|
|
|
|
|
char c = 0;
|
|
|
|
|
struct fuse_file_info tmpfi;
|
|
|
|
|
struct fuse_file_info *openfi = fi;
|
|
|
|
|
if (!fi) {
|
|
|
|
|
openfi = &tmpfi;
|
|
|
|
|
openfi->flags = O_WRONLY;
|
|
|
|
|
res = sshfs_open(path, openfi);
|
|
|
|
|
if (res)
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
res = sshfs_write(path, &c, 1, size - 1, openfi);
|
|
|
|
|
if (res == 1)
|
|
|
|
|
res = sshfs_flush(path, openfi);
|
|
|
|
|
if (!fi)
|
|
|
|
|
sshfs_release(path, openfi);
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Work around broken sftp servers which don't handle
|
|
|
|
|
* SSH_FILEXFER_ATTR_SIZE in SETSTAT request.
|
|
|
|
|
*
|
|
|
|
|
* If new size is zero, just open the file with O_TRUNC.
|
|
|
|
|
*
|
|
|
|
|
* If new size is smaller than current size, then copy file locally,
|
|
|
|
|
* then open/trunc and send it back.
|
|
|
|
|
*
|
|
|
|
|
* If new size is greater than current size, then write a zero byte to
|
|
|
|
|
* the new end of the file.
|
|
|
|
|
*/
|
|
|
|
|
static int sshfs_truncate_workaround(const char *path, off_t size,
|
|
|
|
|
struct fuse_file_info *fi)
|
|
|
|
|
{
|
|
|
|
|
if (size == 0)
|
|
|
|
|
return sshfs_truncate_zero(path);
|
|
|
|
|
else {
|
|
|
|
|
struct stat stbuf;
|
|
|
|
|
int err;
|
|
|
|
|
if (fi)
|
|
|
|
|
err = sshfs_fgetattr(path, &stbuf, fi);
|
|
|
|
|
else
|
|
|
|
|
err = sshfs_getattr(path, &stbuf);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
if (stbuf.st_size == size)
|
|
|
|
|
return 0;
|
|
|
|
|
else if (stbuf.st_size > size)
|
|
|
|
|
return sshfs_truncate_shrink(path, size);
|
|
|
|
|
else
|
|
|
|
|
return sshfs_truncate_extend(path, size, fi);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int processing_init(void)
|
|
|
|
|
{
|
|
|
|
@@ -2154,6 +2285,7 @@ static void usage(const char *progname)
|
|
|
|
|
" all all workarounds enabled\n"
|
|
|
|
|
" [no]rename fix renaming to existing file (default: off)\n"
|
|
|
|
|
" [no]nodelay set nodelay tcp flag in ssh (default: on)\n"
|
|
|
|
|
" [no]truncate fix truncate for old servers (default: off)\n"
|
|
|
|
|
" -o idmap=TYPE user/group ID mapping, possible types are:\n"
|
|
|
|
|
" none no translation of the ID space (default)\n"
|
|
|
|
|
" user only translate UID of connecting user\n"
|
|
|
|
@@ -2300,6 +2432,7 @@ int main(int argc, char *argv[])
|
|
|
|
|
sshfs.max_read = 65536;
|
|
|
|
|
sshfs.nodelay_workaround = 1;
|
|
|
|
|
sshfs.rename_workaround = 0;
|
|
|
|
|
sshfs.truncate_workaround = 0;
|
|
|
|
|
sshfs.ssh_ver = 2;
|
|
|
|
|
sshfs.progname = argv[0];
|
|
|
|
|
ssh_add_arg("ssh");
|
|
|
|
|