static ssize_t real_write_file(files_struct *fsp,const char *data, SMB_OFF_T pos, size_t n) { ssize_t ret; if (pos == -1) { ret = vfs_write_data(fsp, data, n); } else { fsp->fh->pos = pos; if (pos && lp_strict_allocate(SNUM(fsp->conn))) { if (vfs_fill_sparse(fsp, pos) == -1) { return -1; } } ret = vfs_pwrite_data(fsp, data, n, pos); } DEBUG(10,("real_write_file (%s): pos = %.0f, size = %lu, returned %ld\n", fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret )); if (ret != -1) { fsp->fh->pos += ret; /* * It turns out that setting the last write time from a Windows * client stops any subsequent writes from updating the write time. * Doing this after the write gives a race condition here where * a stat may see the changed write time before we reset it here, * but it's cheaper than having to store the write time in shared * memory and look it up using dev/inode across all running smbd's. * The 99% solution will hopefully be good enough in this case. JRA. */ if (fsp->pending_modtime) { set_filetime(fsp->conn, fsp->fsp_name, fsp->pending_modtime); /* If we didn't get the "set modtime" call ourselves, we must store the last write time to restore on close. JRA. */ if (!fsp->pending_modtime_owner) { fsp->last_write_time = time(NULL); } } /* Yes - this is correct - writes don't update this. JRA. */ /* Found by Samba4 tests. */ #if 0 fsp->position_information = fsp->pos; #endif } return ret; }
static ssize_t real_write_file(struct smb_request *req, files_struct *fsp, const char *data, SMB_OFF_T pos, size_t n) { ssize_t ret; if (pos == -1) { ret = vfs_write_data(req, fsp, data, n); } else { fsp->fh->pos = pos; if (pos && lp_strict_allocate(SNUM(fsp->conn) && !fsp->is_sparse)) { if (vfs_fill_sparse(fsp, pos) == -1) { return -1; } } ret = vfs_pwrite_data(req, fsp, data, n, pos); } DEBUG(10,("real_write_file (%s): pos = %.0f, size = %lu, returned %ld\n", fsp_str_dbg(fsp), (double)pos, (unsigned long)n, (long)ret)); if (ret != -1) { fsp->fh->pos += ret; /* Yes - this is correct - writes don't update this. JRA. */ /* Found by Samba4 tests. */ #if 0 fsp->position_information = fsp->pos; #endif } return ret; }
int vfswrap_ftruncate(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_OFF_T len) { int result = -1; SMB_STRUCT_STAT st; char c = 0; SMB_OFF_T currpos; START_PROFILE(syscall_ftruncate); if (lp_strict_allocate(SNUM(fsp->conn))) { result = strict_allocate_ftruncate(handle, 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; /* According to W. R. Stevens advanced UNIX prog. Pure 4.3 BSD cannot extend a file with ftruncate. Provide alternate implementation for this */ currpos = SMB_VFS_LSEEK(fsp, fd, 0, SEEK_CUR); if (currpos == -1) { goto done; } /* Do an fstat to see if the file is longer than the requested 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 (SMB_VFS_FSTAT(fsp, fd, &st) == -1) { goto done; } #ifdef S_ISFIFO if (S_ISFIFO(st.st_mode)) { result = 0; goto done; } #endif if (st.st_size == len) { result = 0; goto done; } if (st.st_size > len) { /* the sys_ftruncate should have worked */ goto done; } if (SMB_VFS_LSEEK(fsp, fd, len-1, SEEK_SET) != len -1) goto done; if (SMB_VFS_WRITE(fsp, fd, &c, 1)!=1) goto done; /* Seek to where we were */ if (SMB_VFS_LSEEK(fsp, fd, currpos, SEEK_SET) != currpos) goto done; result = 0; done: END_PROFILE(syscall_ftruncate); return result; }
static NTSTATUS fsctl_zero_data(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct files_struct *fsp, DATA_BLOB *in_input) { struct file_zero_data_info zdata_info; enum ndr_err_code ndr_ret; struct lock_struct lck; int mode; uint64_t len; int ret; NTSTATUS status; if (fsp == NULL) { return NT_STATUS_FILE_CLOSED; } /* WRITE_DATA permission is required */ status = check_access_fsp(fsp, FILE_WRITE_DATA); if (!NT_STATUS_IS_OK(status)) { return status; } /* allow regardless of whether FS supports sparse or not */ ndr_ret = ndr_pull_struct_blob(in_input, mem_ctx, &zdata_info, (ndr_pull_flags_fn_t)ndr_pull_file_zero_data_info); if (ndr_ret != NDR_ERR_SUCCESS) { DEBUG(0, ("failed to unmarshall zero data request\n")); return NT_STATUS_INVALID_PARAMETER; } if (zdata_info.beyond_final_zero < zdata_info.file_off) { DEBUG(0, ("invalid zero data params: off %lu, bfz, %lu\n", (unsigned long)zdata_info.file_off, (unsigned long)zdata_info.beyond_final_zero)); return NT_STATUS_INVALID_PARAMETER; } /* convert strange "beyond final zero" param into length */ len = zdata_info.beyond_final_zero - zdata_info.file_off; if (len == 0) { DEBUG(2, ("zero data called with zero length range\n")); return NT_STATUS_OK; } init_strict_lock_struct(fsp, fsp->op->global->open_persistent_id, zdata_info.file_off, len, WRITE_LOCK, &lck); if (!SMB_VFS_STRICT_LOCK(fsp->conn, fsp, &lck)) { DEBUG(2, ("failed to lock range for zero-data\n")); return NT_STATUS_FILE_LOCK_CONFLICT; } /* * MS-FSCC <58> Section 2.3.65 * This FSCTL sets the range of bytes to zero (0) without extending the * file size. * * The VFS_FALLOCATE_FL_KEEP_SIZE flag is used to satisfy this * constraint. */ mode = VFS_FALLOCATE_FL_PUNCH_HOLE | VFS_FALLOCATE_FL_KEEP_SIZE; ret = SMB_VFS_FALLOCATE(fsp, mode, zdata_info.file_off, len); if (ret == -1) { status = map_nt_error_from_unix_common(errno); DEBUG(2, ("zero-data fallocate(0x%x) failed: %s\n", mode, strerror(errno))); SMB_VFS_STRICT_UNLOCK(fsp->conn, fsp, &lck); return status; } if (!fsp->is_sparse && lp_strict_allocate(SNUM(fsp->conn))) { /* * File marked non-sparse and "strict allocate" is enabled - * allocate the range that we just punched out. * In future FALLOC_FL_ZERO_RANGE could be used exclusively for * this, but it's currently only supported on XFS and ext4. * * The newly allocated range still won't be found by SEEK_DATA * for QAR, but stat.st_blocks will reflect it. */ ret = SMB_VFS_FALLOCATE(fsp, VFS_FALLOCATE_FL_KEEP_SIZE, zdata_info.file_off, len); if (ret == -1) { status = map_nt_error_from_unix_common(errno); DEBUG(0, ("fallocate failed: %s\n", strerror(errno))); SMB_VFS_STRICT_UNLOCK(fsp->conn, fsp, &lck); return status; } } SMB_VFS_STRICT_UNLOCK(fsp->conn, fsp, &lck); return NT_STATUS_OK; }
int vfs_allocate_file_space(files_struct *fsp, SMB_BIG_UINT len) { int ret; SMB_STRUCT_STAT st; connection_struct *conn = fsp->conn; SMB_BIG_UINT space_avail; SMB_BIG_UINT bsize,dfree,dsize; release_level_2_oplocks_on_change(fsp); /* * Actually try and commit the space on disk.... */ DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp->fsp_name, (double)len )); /* Foxconn removed start pling 11/18/2009 */ /* Don't check negative length, to avoid "disk full" error * when copy file from Vista/Win7 to USB. */ #if 0 if (((SMB_OFF_T)len) < 0) { DEBUG(0,("vfs_allocate_file_space: %s negative len requested.\n", fsp->fsp_name )); errno = EINVAL; return -1; } #endif /* Foxconn removed end pling 11/18/2009 */ ret = SMB_VFS_FSTAT(fsp,fsp->fh->fd,&st); if (ret == -1) return ret; if (len == (SMB_BIG_UINT)st.st_size) return 0; if (len < (SMB_BIG_UINT)st.st_size) { /* Shrink - use ftruncate. */ DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current size %.0f\n", fsp->fsp_name, (double)st.st_size )); flush_write_cache(fsp, SIZECHANGE_FLUSH); if ((ret = SMB_VFS_FTRUNCATE(fsp, fsp->fh->fd, (SMB_OFF_T)len)) != -1) { set_filelen_write_cache(fsp, len); } return ret; } /* Grow - we need to test if we have enough space. */ if (!lp_strict_allocate(SNUM(fsp->conn))) return 0; len -= st.st_size; len /= 1024; /* Len is now number of 1k blocks needed. */ space_avail = get_dfree_info(conn,fsp->fsp_name,False,&bsize,&dfree,&dsize); if (space_avail == (SMB_BIG_UINT)-1) { return -1; } DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, needed blocks = %.0f, space avail = %.0f\n", fsp->fsp_name, (double)st.st_size, (double)len, (double)space_avail )); if (len > space_avail) { errno = ENOSPC; return -1; } return 0; }
int vfs_allocate_file_space(files_struct *fsp, uint64_t len) { int ret; SMB_STRUCT_STAT st; connection_struct *conn = fsp->conn; uint64_t space_avail; uint64_t bsize,dfree,dsize; /* * Actually try and commit the space on disk.... */ DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp_str_dbg(fsp), (double)len)); if (((SMB_OFF_T)len) < 0) { DEBUG(0,("vfs_allocate_file_space: %s negative len " "requested.\n", fsp_str_dbg(fsp))); errno = EINVAL; return -1; } ret = SMB_VFS_FSTAT(fsp, &st); if (ret == -1) return ret; if (len == (uint64_t)st.st_ex_size) return 0; if (len < (uint64_t)st.st_ex_size) { /* Shrink - use ftruncate. */ DEBUG(10,("vfs_allocate_file_space: file %s, shrink. Current " "size %.0f\n", fsp_str_dbg(fsp), (double)st.st_ex_size)); contend_level2_oplocks_begin(fsp, LEVEL2_CONTEND_ALLOC_SHRINK); flush_write_cache(fsp, SIZECHANGE_FLUSH); if ((ret = SMB_VFS_FTRUNCATE(fsp, (SMB_OFF_T)len)) != -1) { set_filelen_write_cache(fsp, len); } contend_level2_oplocks_end(fsp, LEVEL2_CONTEND_ALLOC_SHRINK); return ret; } /* Grow - we need to test if we have enough space. */ contend_level2_oplocks_begin(fsp, LEVEL2_CONTEND_ALLOC_GROW); contend_level2_oplocks_end(fsp, LEVEL2_CONTEND_ALLOC_GROW); if (!lp_strict_allocate(SNUM(fsp->conn))) return 0; len -= st.st_ex_size; len /= 1024; /* Len is now number of 1k blocks needed. */ space_avail = get_dfree_info(conn, fsp->fsp_name->base_name, false, &bsize, &dfree, &dsize); if (space_avail == (uint64_t)-1) { return -1; } DEBUG(10,("vfs_allocate_file_space: file %s, grow. Current size %.0f, " "needed blocks = %.0f, space avail = %.0f\n", fsp_str_dbg(fsp), (double)st.st_ex_size, (double)len, (double)space_avail)); if (len > space_avail) { errno = ENOSPC; return -1; } return 0; }