| .. | .. |
|---|
| 7 | 7 | #include <linux/fs.h> |
|---|
| 8 | 8 | #include <linux/file.h> |
|---|
| 9 | 9 | #include <linux/falloc.h> |
|---|
| 10 | +#include <linux/mount.h> |
|---|
| 10 | 11 | #include <linux/nfs_fs.h> |
|---|
| 12 | +#include <linux/nfs_ssc.h> |
|---|
| 11 | 13 | #include "delegation.h" |
|---|
| 12 | 14 | #include "internal.h" |
|---|
| 13 | 15 | #include "iostat.h" |
|---|
| .. | .. |
|---|
| 30 | 32 | struct dentry *parent = NULL; |
|---|
| 31 | 33 | struct inode *dir; |
|---|
| 32 | 34 | unsigned openflags = filp->f_flags; |
|---|
| 35 | + fmode_t f_mode; |
|---|
| 33 | 36 | struct iattr attr; |
|---|
| 34 | 37 | int err; |
|---|
| 35 | 38 | |
|---|
| .. | .. |
|---|
| 48 | 51 | if (err) |
|---|
| 49 | 52 | return err; |
|---|
| 50 | 53 | |
|---|
| 54 | + f_mode = filp->f_mode; |
|---|
| 51 | 55 | if ((openflags & O_ACCMODE) == 3) |
|---|
| 52 | | - return nfs_open(inode, filp); |
|---|
| 56 | + f_mode |= flags_to_mode(openflags); |
|---|
| 53 | 57 | |
|---|
| 54 | 58 | /* We can't create new files here */ |
|---|
| 55 | 59 | openflags &= ~(O_CREAT|O_EXCL); |
|---|
| .. | .. |
|---|
| 57 | 61 | parent = dget_parent(dentry); |
|---|
| 58 | 62 | dir = d_inode(parent); |
|---|
| 59 | 63 | |
|---|
| 60 | | - ctx = alloc_nfs_open_context(file_dentry(filp), filp->f_mode, filp); |
|---|
| 64 | + ctx = alloc_nfs_open_context(file_dentry(filp), f_mode, filp); |
|---|
| 61 | 65 | err = PTR_ERR(ctx); |
|---|
| 62 | 66 | if (IS_ERR(ctx)) |
|---|
| 63 | 67 | goto out; |
|---|
| .. | .. |
|---|
| 86 | 90 | if (inode != d_inode(dentry)) |
|---|
| 87 | 91 | goto out_drop; |
|---|
| 88 | 92 | |
|---|
| 89 | | - nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); |
|---|
| 90 | 93 | nfs_file_set_open_context(filp, ctx); |
|---|
| 91 | 94 | nfs_fscache_open_file(inode, filp); |
|---|
| 92 | 95 | err = 0; |
|---|
| .. | .. |
|---|
| 110 | 113 | nfs4_file_flush(struct file *file, fl_owner_t id) |
|---|
| 111 | 114 | { |
|---|
| 112 | 115 | struct inode *inode = file_inode(file); |
|---|
| 116 | + errseq_t since; |
|---|
| 113 | 117 | |
|---|
| 114 | 118 | dprintk("NFS: flush(%pD2)\n", file); |
|---|
| 115 | 119 | |
|---|
| .. | .. |
|---|
| 125 | 129 | return filemap_fdatawrite(file->f_mapping); |
|---|
| 126 | 130 | |
|---|
| 127 | 131 | /* Flush writes to the server and return any errors */ |
|---|
| 128 | | - return vfs_fsync(file, 0); |
|---|
| 132 | + since = filemap_sample_wb_err(file->f_mapping); |
|---|
| 133 | + nfs_wb_all(inode); |
|---|
| 134 | + return filemap_check_wb_err(file->f_mapping, since); |
|---|
| 129 | 135 | } |
|---|
| 130 | 136 | |
|---|
| 131 | 137 | #ifdef CONFIG_NFS_V4_2 |
|---|
| 138 | +static ssize_t __nfs4_copy_file_range(struct file *file_in, loff_t pos_in, |
|---|
| 139 | + struct file *file_out, loff_t pos_out, |
|---|
| 140 | + size_t count, unsigned int flags) |
|---|
| 141 | +{ |
|---|
| 142 | + struct nfs42_copy_notify_res *cn_resp = NULL; |
|---|
| 143 | + struct nl4_server *nss = NULL; |
|---|
| 144 | + nfs4_stateid *cnrs = NULL; |
|---|
| 145 | + ssize_t ret; |
|---|
| 146 | + bool sync = false; |
|---|
| 147 | + |
|---|
| 148 | + /* Only offload copy if superblock is the same */ |
|---|
| 149 | + if (file_in->f_op != &nfs4_file_operations) |
|---|
| 150 | + return -EXDEV; |
|---|
| 151 | + if (!nfs_server_capable(file_inode(file_out), NFS_CAP_COPY) || |
|---|
| 152 | + !nfs_server_capable(file_inode(file_in), NFS_CAP_COPY)) |
|---|
| 153 | + return -EOPNOTSUPP; |
|---|
| 154 | + if (file_inode(file_in) == file_inode(file_out)) |
|---|
| 155 | + return -EOPNOTSUPP; |
|---|
| 156 | + /* if the copy size if smaller than 2 RPC payloads, make it |
|---|
| 157 | + * synchronous |
|---|
| 158 | + */ |
|---|
| 159 | + if (count <= 2 * NFS_SERVER(file_inode(file_in))->rsize) |
|---|
| 160 | + sync = true; |
|---|
| 161 | +retry: |
|---|
| 162 | + if (!nfs42_files_from_same_server(file_in, file_out)) { |
|---|
| 163 | + /* for inter copy, if copy size if smaller than 12 RPC |
|---|
| 164 | + * payloads, fallback to traditional copy. There are |
|---|
| 165 | + * 14 RPCs during an NFSv4.x mount between source/dest |
|---|
| 166 | + * servers. |
|---|
| 167 | + */ |
|---|
| 168 | + if (sync || |
|---|
| 169 | + count <= 14 * NFS_SERVER(file_inode(file_in))->rsize) |
|---|
| 170 | + return -EOPNOTSUPP; |
|---|
| 171 | + cn_resp = kzalloc(sizeof(struct nfs42_copy_notify_res), |
|---|
| 172 | + GFP_NOFS); |
|---|
| 173 | + if (unlikely(cn_resp == NULL)) |
|---|
| 174 | + return -ENOMEM; |
|---|
| 175 | + |
|---|
| 176 | + ret = nfs42_proc_copy_notify(file_in, file_out, cn_resp); |
|---|
| 177 | + if (ret) { |
|---|
| 178 | + ret = -EOPNOTSUPP; |
|---|
| 179 | + goto out; |
|---|
| 180 | + } |
|---|
| 181 | + nss = &cn_resp->cnr_src; |
|---|
| 182 | + cnrs = &cn_resp->cnr_stateid; |
|---|
| 183 | + } |
|---|
| 184 | + ret = nfs42_proc_copy(file_in, pos_in, file_out, pos_out, count, |
|---|
| 185 | + nss, cnrs, sync); |
|---|
| 186 | +out: |
|---|
| 187 | + if (!nfs42_files_from_same_server(file_in, file_out)) |
|---|
| 188 | + kfree(cn_resp); |
|---|
| 189 | + if (ret == -EAGAIN) |
|---|
| 190 | + goto retry; |
|---|
| 191 | + return ret; |
|---|
| 192 | +} |
|---|
| 193 | + |
|---|
| 132 | 194 | static ssize_t nfs4_copy_file_range(struct file *file_in, loff_t pos_in, |
|---|
| 133 | 195 | struct file *file_out, loff_t pos_out, |
|---|
| 134 | 196 | size_t count, unsigned int flags) |
|---|
| 135 | 197 | { |
|---|
| 136 | | - if (!nfs_server_capable(file_inode(file_out), NFS_CAP_COPY)) |
|---|
| 137 | | - return -EOPNOTSUPP; |
|---|
| 138 | | - if (file_inode(file_in) == file_inode(file_out)) |
|---|
| 139 | | - return -EOPNOTSUPP; |
|---|
| 140 | | - return nfs42_proc_copy(file_in, pos_in, file_out, pos_out, count); |
|---|
| 198 | + ssize_t ret; |
|---|
| 199 | + |
|---|
| 200 | + ret = __nfs4_copy_file_range(file_in, pos_in, file_out, pos_out, count, |
|---|
| 201 | + flags); |
|---|
| 202 | + if (ret == -EOPNOTSUPP || ret == -EXDEV) |
|---|
| 203 | + ret = generic_copy_file_range(file_in, pos_in, file_out, |
|---|
| 204 | + pos_out, count, flags); |
|---|
| 205 | + return ret; |
|---|
| 141 | 206 | } |
|---|
| 142 | 207 | |
|---|
| 143 | 208 | static loff_t nfs4_file_llseek(struct file *filep, loff_t offset, int whence) |
|---|
| .. | .. |
|---|
| 150 | 215 | ret = nfs42_proc_llseek(filep, offset, whence); |
|---|
| 151 | 216 | if (ret != -EOPNOTSUPP) |
|---|
| 152 | 217 | return ret; |
|---|
| 153 | | - /* Fall through */ |
|---|
| 218 | + fallthrough; |
|---|
| 154 | 219 | default: |
|---|
| 155 | 220 | return nfs_file_llseek(filep, offset, whence); |
|---|
| 156 | 221 | } |
|---|
| .. | .. |
|---|
| 176 | 241 | return nfs42_proc_allocate(filep, offset, len); |
|---|
| 177 | 242 | } |
|---|
| 178 | 243 | |
|---|
| 179 | | -static int nfs42_clone_file_range(struct file *src_file, loff_t src_off, |
|---|
| 180 | | - struct file *dst_file, loff_t dst_off, u64 count) |
|---|
| 244 | +static loff_t nfs42_remap_file_range(struct file *src_file, loff_t src_off, |
|---|
| 245 | + struct file *dst_file, loff_t dst_off, loff_t count, |
|---|
| 246 | + unsigned int remap_flags) |
|---|
| 181 | 247 | { |
|---|
| 182 | 248 | struct inode *dst_inode = file_inode(dst_file); |
|---|
| 183 | 249 | struct nfs_server *server = NFS_SERVER(dst_inode); |
|---|
| .. | .. |
|---|
| 185 | 251 | unsigned int bs = server->clone_blksize; |
|---|
| 186 | 252 | bool same_inode = false; |
|---|
| 187 | 253 | int ret; |
|---|
| 254 | + |
|---|
| 255 | + /* NFS does not support deduplication. */ |
|---|
| 256 | + if (remap_flags & REMAP_FILE_DEDUP) |
|---|
| 257 | + return -EOPNOTSUPP; |
|---|
| 258 | + |
|---|
| 259 | + if (remap_flags & ~REMAP_FILE_ADVISORY) |
|---|
| 260 | + return -EINVAL; |
|---|
| 261 | + |
|---|
| 262 | + if (IS_SWAPFILE(dst_inode) || IS_SWAPFILE(src_inode)) |
|---|
| 263 | + return -ETXTBSY; |
|---|
| 188 | 264 | |
|---|
| 189 | 265 | /* check alignment w.r.t. clone_blksize */ |
|---|
| 190 | 266 | ret = -EINVAL; |
|---|
| .. | .. |
|---|
| 236 | 312 | inode_unlock(src_inode); |
|---|
| 237 | 313 | } |
|---|
| 238 | 314 | out: |
|---|
| 239 | | - return ret; |
|---|
| 315 | + return ret < 0 ? ret : count; |
|---|
| 316 | +} |
|---|
| 317 | + |
|---|
| 318 | +static int read_name_gen = 1; |
|---|
| 319 | +#define SSC_READ_NAME_BODY "ssc_read_%d" |
|---|
| 320 | + |
|---|
| 321 | +static struct file *__nfs42_ssc_open(struct vfsmount *ss_mnt, |
|---|
| 322 | + struct nfs_fh *src_fh, nfs4_stateid *stateid) |
|---|
| 323 | +{ |
|---|
| 324 | + struct nfs_fattr *fattr = nfs_alloc_fattr(); |
|---|
| 325 | + struct file *filep, *res; |
|---|
| 326 | + struct nfs_server *server; |
|---|
| 327 | + struct inode *r_ino = NULL; |
|---|
| 328 | + struct nfs_open_context *ctx; |
|---|
| 329 | + struct nfs4_state_owner *sp; |
|---|
| 330 | + char *read_name = NULL; |
|---|
| 331 | + int len, status = 0; |
|---|
| 332 | + |
|---|
| 333 | + server = NFS_SERVER(ss_mnt->mnt_root->d_inode); |
|---|
| 334 | + |
|---|
| 335 | + if (!fattr) |
|---|
| 336 | + return ERR_PTR(-ENOMEM); |
|---|
| 337 | + |
|---|
| 338 | + status = nfs4_proc_getattr(server, src_fh, fattr, NULL, NULL); |
|---|
| 339 | + if (status < 0) { |
|---|
| 340 | + res = ERR_PTR(status); |
|---|
| 341 | + goto out; |
|---|
| 342 | + } |
|---|
| 343 | + |
|---|
| 344 | + if (!S_ISREG(fattr->mode)) { |
|---|
| 345 | + res = ERR_PTR(-EBADF); |
|---|
| 346 | + goto out; |
|---|
| 347 | + } |
|---|
| 348 | + |
|---|
| 349 | + res = ERR_PTR(-ENOMEM); |
|---|
| 350 | + len = strlen(SSC_READ_NAME_BODY) + 16; |
|---|
| 351 | + read_name = kzalloc(len, GFP_NOFS); |
|---|
| 352 | + if (read_name == NULL) |
|---|
| 353 | + goto out; |
|---|
| 354 | + snprintf(read_name, len, SSC_READ_NAME_BODY, read_name_gen++); |
|---|
| 355 | + |
|---|
| 356 | + r_ino = nfs_fhget(ss_mnt->mnt_root->d_inode->i_sb, src_fh, fattr, |
|---|
| 357 | + NULL); |
|---|
| 358 | + if (IS_ERR(r_ino)) { |
|---|
| 359 | + res = ERR_CAST(r_ino); |
|---|
| 360 | + goto out_free_name; |
|---|
| 361 | + } |
|---|
| 362 | + |
|---|
| 363 | + filep = alloc_file_pseudo(r_ino, ss_mnt, read_name, FMODE_READ, |
|---|
| 364 | + r_ino->i_fop); |
|---|
| 365 | + if (IS_ERR(filep)) { |
|---|
| 366 | + res = ERR_CAST(filep); |
|---|
| 367 | + iput(r_ino); |
|---|
| 368 | + goto out_free_name; |
|---|
| 369 | + } |
|---|
| 370 | + filep->f_mode |= FMODE_READ; |
|---|
| 371 | + |
|---|
| 372 | + ctx = alloc_nfs_open_context(filep->f_path.dentry, filep->f_mode, |
|---|
| 373 | + filep); |
|---|
| 374 | + if (IS_ERR(ctx)) { |
|---|
| 375 | + res = ERR_CAST(ctx); |
|---|
| 376 | + goto out_filep; |
|---|
| 377 | + } |
|---|
| 378 | + |
|---|
| 379 | + res = ERR_PTR(-EINVAL); |
|---|
| 380 | + sp = nfs4_get_state_owner(server, ctx->cred, GFP_KERNEL); |
|---|
| 381 | + if (sp == NULL) |
|---|
| 382 | + goto out_ctx; |
|---|
| 383 | + |
|---|
| 384 | + ctx->state = nfs4_get_open_state(r_ino, sp); |
|---|
| 385 | + if (ctx->state == NULL) |
|---|
| 386 | + goto out_stateowner; |
|---|
| 387 | + |
|---|
| 388 | + set_bit(NFS_SRV_SSC_COPY_STATE, &ctx->state->flags); |
|---|
| 389 | + memcpy(&ctx->state->open_stateid.other, &stateid->other, |
|---|
| 390 | + NFS4_STATEID_OTHER_SIZE); |
|---|
| 391 | + update_open_stateid(ctx->state, stateid, NULL, filep->f_mode); |
|---|
| 392 | + set_bit(NFS_OPEN_STATE, &ctx->state->flags); |
|---|
| 393 | + |
|---|
| 394 | + nfs_file_set_open_context(filep, ctx); |
|---|
| 395 | + put_nfs_open_context(ctx); |
|---|
| 396 | + |
|---|
| 397 | + file_ra_state_init(&filep->f_ra, filep->f_mapping->host->i_mapping); |
|---|
| 398 | + res = filep; |
|---|
| 399 | +out_free_name: |
|---|
| 400 | + kfree(read_name); |
|---|
| 401 | +out: |
|---|
| 402 | + nfs_free_fattr(fattr); |
|---|
| 403 | + return res; |
|---|
| 404 | +out_stateowner: |
|---|
| 405 | + nfs4_put_state_owner(sp); |
|---|
| 406 | +out_ctx: |
|---|
| 407 | + put_nfs_open_context(ctx); |
|---|
| 408 | +out_filep: |
|---|
| 409 | + fput(filep); |
|---|
| 410 | + goto out_free_name; |
|---|
| 411 | +} |
|---|
| 412 | + |
|---|
| 413 | +static void __nfs42_ssc_close(struct file *filep) |
|---|
| 414 | +{ |
|---|
| 415 | + struct nfs_open_context *ctx = nfs_file_open_context(filep); |
|---|
| 416 | + |
|---|
| 417 | + ctx->state->flags = 0; |
|---|
| 418 | +} |
|---|
| 419 | + |
|---|
| 420 | +static const struct nfs4_ssc_client_ops nfs4_ssc_clnt_ops_tbl = { |
|---|
| 421 | + .sco_open = __nfs42_ssc_open, |
|---|
| 422 | + .sco_close = __nfs42_ssc_close, |
|---|
| 423 | +}; |
|---|
| 424 | + |
|---|
| 425 | +/** |
|---|
| 426 | + * nfs42_ssc_register_ops - Wrapper to register NFS_V4 ops in nfs_common |
|---|
| 427 | + * |
|---|
| 428 | + * Return values: |
|---|
| 429 | + * None |
|---|
| 430 | + */ |
|---|
| 431 | +void nfs42_ssc_register_ops(void) |
|---|
| 432 | +{ |
|---|
| 433 | + nfs42_ssc_register(&nfs4_ssc_clnt_ops_tbl); |
|---|
| 434 | +} |
|---|
| 435 | + |
|---|
| 436 | +/** |
|---|
| 437 | + * nfs42_ssc_unregister_ops - wrapper to un-register NFS_V4 ops in nfs_common |
|---|
| 438 | + * |
|---|
| 439 | + * Return values: |
|---|
| 440 | + * None. |
|---|
| 441 | + */ |
|---|
| 442 | +void nfs42_ssc_unregister_ops(void) |
|---|
| 443 | +{ |
|---|
| 444 | + nfs42_ssc_unregister(&nfs4_ssc_clnt_ops_tbl); |
|---|
| 240 | 445 | } |
|---|
| 241 | 446 | #endif /* CONFIG_NFS_V4_2 */ |
|---|
| 242 | 447 | |
|---|
| .. | .. |
|---|
| 258 | 463 | .copy_file_range = nfs4_copy_file_range, |
|---|
| 259 | 464 | .llseek = nfs4_file_llseek, |
|---|
| 260 | 465 | .fallocate = nfs42_fallocate, |
|---|
| 261 | | - .clone_file_range = nfs42_clone_file_range, |
|---|
| 466 | + .remap_file_range = nfs42_remap_file_range, |
|---|
| 262 | 467 | #else |
|---|
| 263 | 468 | .llseek = nfs_file_llseek, |
|---|
| 264 | 469 | #endif |
|---|