.. | .. |
---|
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 |
---|