summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/smbd/vfs-wrap.c78
1 files changed, 70 insertions, 8 deletions
diff --git a/source3/smbd/vfs-wrap.c b/source3/smbd/vfs-wrap.c
index b2d73561af..48d16bdaaf 100644
--- a/source3/smbd/vfs-wrap.c
+++ b/source3/smbd/vfs-wrap.c
@@ -476,6 +476,62 @@ int vfswrap_utime(connection_struct *conn, const char *path, struct utimbuf *tim
return result;
}
+/*********************************************************************
+ A version of ftruncate that will write the space on disk if strict
+ allocate is set.
+**********************************************************************/
+
+static int strict_allocate_ftruncate(files_struct *fsp, int fd, SMB_OFF_T len)
+{
+ struct vfs_ops *vfs_ops = &fsp->conn->vfs_ops;
+ SMB_STRUCT_STAT st;
+ SMB_OFF_T currpos = vfs_ops->lseek(fsp, fd, 0, SEEK_CUR);
+ unsigned char zero_space[4096];
+ SMB_OFF_T space_to_write = len - st.st_size;
+
+ if (currpos == -1)
+ return -1;
+
+ if (vfs_ops->fstat(fsp, fd, &st) == -1)
+ return -1;
+
+#ifdef S_ISFIFO
+ if (S_ISFIFO(st.st_mode))
+ return 0;
+#endif
+
+ if (st.st_size == len)
+ return 0;
+
+ /* Shrink - just ftruncate. */
+ if (st.st_size > len)
+ return sys_ftruncate(fd, len);
+
+ /* Write out the real space on disk. */
+ if (vfs_ops->lseek(fsp, fd, st.st_size, SEEK_SET) != st.st_size)
+ return -1;
+
+ space_to_write = len - st.st_size;
+
+ memset(zero_space, '\0', sizeof(zero_space));
+ while ( space_to_write > 0) {
+ SMB_OFF_T retlen;
+ SMB_OFF_T current_len_to_write = MIN(sizeof(zero_space),space_to_write);
+
+ retlen = vfs_ops->write(fsp,fsp->fd,(char *)zero_space,current_len_to_write);
+ if (retlen <= 0)
+ return -1;
+
+ space_to_write -= retlen;
+ }
+
+ /* Seek to where we were */
+ if (vfs_ops->lseek(fsp, fd, currpos, SEEK_SET) != currpos)
+ return -1;
+
+ return 0;
+}
+
int vfswrap_ftruncate(files_struct *fsp, int fd, SMB_OFF_T len)
{
int result = -1;
@@ -486,13 +542,21 @@ int vfswrap_ftruncate(files_struct *fsp, int fd, SMB_OFF_T len)
START_PROFILE(syscall_ftruncate);
+ if (lp_strict_allocate(SNUM(fsp->conn))) {
+ result = strict_allocate_ftruncate(fsp, fd, len);
+ END_PROFILE(syscall_ftruncate);
+ return result;
+ }
+
/* we used to just check HAVE_FTRUNCATE_EXTEND and only use
sys_ftruncate if the system supports it. Then I discovered that
you can have some filesystems that support ftruncate
expansion and some that don't! On Linux fat can't do
ftruncate extend but ext2 can. */
+
result = sys_ftruncate(fd, len);
- if (result == 0) goto done;
+ if (result == 0)
+ goto done;
/* According to W. R. Stevens advanced UNIX prog. Pure 4.3 BSD cannot
extend a file with ftruncate. Provide alternate implementation
@@ -506,7 +570,7 @@ int vfswrap_ftruncate(files_struct *fsp, int fd, SMB_OFF_T len)
size in which case the ftruncate above should have
succeeded or shorter, in which case seek to len - 1 and
write 1 byte of zero */
- if (vfs_ops->fstat(fsp, fd, &st) < 0) {
+ if (vfs_ops->fstat(fsp, fd, &st) == -1) {
goto done;
}
@@ -527,19 +591,17 @@ int vfswrap_ftruncate(files_struct *fsp, int fd, SMB_OFF_T len)
goto done;
}
- if (vfs_ops->lseek(fsp, fd, len-1, SEEK_SET) != len -1) {
+ if (vfs_ops->lseek(fsp, fd, len-1, SEEK_SET) != len -1)
goto done;
- }
- if (vfs_ops->write(fsp, fd, &c, 1)!=1) {
+ if (vfs_ops->write(fsp, fd, &c, 1)!=1)
goto done;
- }
/* Seek to where we were */
- if (vfs_ops->lseek(fsp, fd, currpos, SEEK_SET) != currpos) {
+ if (vfs_ops->lseek(fsp, fd, currpos, SEEK_SET) != currpos)
goto done;
- }
result = 0;
+
done:
END_PROFILE(syscall_ftruncate);