]>
Commit | Line | Data |
---|---|---|
5ce34554 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
1da177e4 LT |
2 | /* |
3 | * file.c | |
4 | * | |
5 | * PURPOSE | |
6 | * File handling routines for the OSTA-UDF(tm) filesystem. | |
7 | * | |
1da177e4 | 8 | * COPYRIGHT |
1da177e4 LT |
9 | * (C) 1998-1999 Dave Boynton |
10 | * (C) 1998-2004 Ben Fennema | |
11 | * (C) 1999-2000 Stelias Computing Inc | |
12 | * | |
13 | * HISTORY | |
14 | * | |
15 | * 10/02/98 dgb Attempt to integrate into udf.o | |
16 | * 10/07/98 Switched to using generic_readpage, etc., like isofs | |
17 | * And it works! | |
18 | * 12/06/98 blf Added udf_file_read. uses generic_file_read for all cases but | |
19 | * ICBTAG_FLAG_AD_IN_ICB. | |
20 | * 04/06/99 64 bit file handling on 32 bit systems taken from ext2 file.c | |
21 | * 05/12/99 Preliminary file write support | |
22 | */ | |
23 | ||
24 | #include "udfdecl.h" | |
25 | #include <linux/fs.h> | |
e973606c | 26 | #include <linux/uaccess.h> |
1da177e4 | 27 | #include <linux/kernel.h> |
28de7948 | 28 | #include <linux/string.h> /* memset */ |
16f7e0fe | 29 | #include <linux/capability.h> |
1da177e4 | 30 | #include <linux/errno.h> |
1da177e4 | 31 | #include <linux/pagemap.h> |
e2e40f2c | 32 | #include <linux/uio.h> |
1da177e4 LT |
33 | |
34 | #include "udf_i.h" | |
35 | #include "udf_sb.h" | |
36 | ||
3c212048 | 37 | static vm_fault_t udf_page_mkwrite(struct vm_fault *vmf) |
1da177e4 | 38 | { |
3c212048 JK |
39 | struct vm_area_struct *vma = vmf->vma; |
40 | struct inode *inode = file_inode(vma->vm_file); | |
41 | struct address_space *mapping = inode->i_mapping; | |
f5985ef2 | 42 | struct folio *folio = page_folio(vmf->page); |
3c212048 JK |
43 | loff_t size; |
44 | unsigned int end; | |
45 | vm_fault_t ret = VM_FAULT_LOCKED; | |
46 | int err; | |
5eec54fc | 47 | |
3c212048 JK |
48 | sb_start_pagefault(inode->i_sb); |
49 | file_update_time(vma->vm_file); | |
50 | filemap_invalidate_lock_shared(mapping); | |
f5985ef2 | 51 | folio_lock(folio); |
3c212048 | 52 | size = i_size_read(inode); |
f5985ef2 MWO |
53 | if (folio->mapping != inode->i_mapping || folio_pos(folio) >= size) { |
54 | folio_unlock(folio); | |
3c212048 JK |
55 | ret = VM_FAULT_NOPAGE; |
56 | goto out_unlock; | |
57 | } | |
58 | /* Space is already allocated for in-ICB file */ | |
59 | if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) | |
60 | goto out_dirty; | |
f5985ef2 | 61 | if (folio->index == size >> PAGE_SHIFT) |
3c212048 JK |
62 | end = size & ~PAGE_MASK; |
63 | else | |
64 | end = PAGE_SIZE; | |
f5985ef2 | 65 | err = __block_write_begin(&folio->page, 0, end, udf_get_block); |
a524fcfe | 66 | if (err) { |
f5985ef2 | 67 | folio_unlock(folio); |
2ba39cc4 | 68 | ret = vmf_fs_error(err); |
3c212048 JK |
69 | goto out_unlock; |
70 | } | |
a524fcfe | 71 | |
f5985ef2 | 72 | block_commit_write(&folio->page, 0, end); |
3c212048 | 73 | out_dirty: |
f5985ef2 MWO |
74 | folio_mark_dirty(folio); |
75 | folio_wait_stable(folio); | |
3c212048 JK |
76 | out_unlock: |
77 | filemap_invalidate_unlock_shared(mapping); | |
78 | sb_end_pagefault(inode->i_sb); | |
79 | return ret; | |
5d3ddd84 AV |
80 | } |
81 | ||
3c212048 JK |
82 | static const struct vm_operations_struct udf_file_vm_ops = { |
83 | .fault = filemap_fault, | |
84 | .map_pages = filemap_map_pages, | |
85 | .page_mkwrite = udf_page_mkwrite, | |
1da177e4 LT |
86 | }; |
87 | ||
d4637bc1 | 88 | static ssize_t udf_file_write_iter(struct kiocb *iocb, struct iov_iter *from) |
1da177e4 LT |
89 | { |
90 | ssize_t retval; | |
543ade1f | 91 | struct file *file = iocb->ki_filp; |
496ad9aa | 92 | struct inode *inode = file_inode(file); |
48d6d8ff | 93 | struct udf_inode_info *iinfo = UDF_I(inode); |
1da177e4 | 94 | |
5955102c | 95 | inode_lock(inode); |
165f1a6e | 96 | |
3309dd04 AV |
97 | retval = generic_write_checks(iocb, from); |
98 | if (retval <= 0) | |
165f1a6e AV |
99 | goto out; |
100 | ||
256fe416 JK |
101 | if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB && |
102 | inode->i_sb->s_blocksize < (udf_file_entry_alloc_offset(inode) + | |
103 | iocb->ki_pos + iov_iter_count(from))) { | |
b9a861fd | 104 | filemap_invalidate_lock(inode->i_mapping); |
96eeaaae | 105 | retval = udf_expand_file_adinicb(inode); |
b9a861fd | 106 | filemap_invalidate_unlock(inode->i_mapping); |
96eeaaae JK |
107 | if (retval) |
108 | goto out; | |
6a3b37e0 | 109 | } |
1da177e4 | 110 | |
d4637bc1 | 111 | retval = __generic_file_write_iter(iocb, from); |
5f380c7f | 112 | out: |
6a3b37e0 JK |
113 | if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB && retval > 0) { |
114 | down_write(&iinfo->i_data_sem); | |
256fe416 | 115 | iinfo->i_lenAlloc = inode->i_size; |
6a3b37e0 JK |
116 | up_write(&iinfo->i_data_sem); |
117 | } | |
5955102c | 118 | inode_unlock(inode); |
09ebb17a JK |
119 | |
120 | if (retval > 0) { | |
1da177e4 | 121 | mark_inode_dirty(inode); |
e2592217 | 122 | retval = generic_write_sync(iocb, retval); |
09ebb17a | 123 | } |
28de7948 | 124 | |
1da177e4 LT |
125 | return retval; |
126 | } | |
127 | ||
2f07a88b | 128 | long udf_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
1da177e4 | 129 | { |
496ad9aa | 130 | struct inode *inode = file_inode(filp); |
28de7948 | 131 | long old_block, new_block; |
a074faad | 132 | int result; |
1da177e4 | 133 | |
02f92b38 | 134 | if (file_permission(filp, MAY_READ) != 0) { |
2f07a88b | 135 | udf_debug("no permission to access inode %lu\n", inode->i_ino); |
a074faad | 136 | return -EPERM; |
1da177e4 LT |
137 | } |
138 | ||
782deb2e FF |
139 | if (!arg && ((cmd == UDF_GETVOLIDENT) || (cmd == UDF_GETEASIZE) || |
140 | (cmd == UDF_RELOCATE_BLOCKS) || (cmd == UDF_GETEABLOCK))) { | |
1da177e4 | 141 | udf_debug("invalid argument to udf_ioctl\n"); |
a074faad | 142 | return -EINVAL; |
1da177e4 LT |
143 | } |
144 | ||
cb00ea35 CG |
145 | switch (cmd) { |
146 | case UDF_GETVOLIDENT: | |
4b11111a MS |
147 | if (copy_to_user((char __user *)arg, |
148 | UDF_SB(inode->i_sb)->s_volume_ident, 32)) | |
a074faad FF |
149 | return -EFAULT; |
150 | return 0; | |
cb00ea35 | 151 | case UDF_RELOCATE_BLOCKS: |
a074faad FF |
152 | if (!capable(CAP_SYS_ADMIN)) |
153 | return -EPERM; | |
154 | if (get_user(old_block, (long __user *)arg)) | |
155 | return -EFAULT; | |
4b11111a MS |
156 | result = udf_relocate_blocks(inode->i_sb, |
157 | old_block, &new_block); | |
158 | if (result == 0) | |
28de7948 | 159 | result = put_user(new_block, (long __user *)arg); |
a074faad | 160 | return result; |
cb00ea35 | 161 | case UDF_GETEASIZE: |
a074faad | 162 | return put_user(UDF_I(inode)->i_lenEAttr, (int __user *)arg); |
cb00ea35 | 163 | case UDF_GETEABLOCK: |
a074faad | 164 | return copy_to_user((char __user *)arg, |
382a2287 | 165 | UDF_I(inode)->i_data, |
a074faad | 166 | UDF_I(inode)->i_lenEAttr) ? -EFAULT : 0; |
782deb2e FF |
167 | default: |
168 | return -ENOIOCTLCMD; | |
1da177e4 LT |
169 | } |
170 | ||
a074faad | 171 | return 0; |
1da177e4 LT |
172 | } |
173 | ||
cb00ea35 | 174 | static int udf_release_file(struct inode *inode, struct file *filp) |
1da177e4 | 175 | { |
6fb1ca92 | 176 | if (filp->f_mode & FMODE_WRITE && |
b07ef352 | 177 | atomic_read(&inode->i_writecount) == 1) { |
6fb1ca92 JK |
178 | /* |
179 | * Grab i_mutex to avoid races with writes changing i_size | |
180 | * while we are running. | |
181 | */ | |
5955102c | 182 | inode_lock(inode); |
4d0fb621 | 183 | down_write(&UDF_I(inode)->i_data_sem); |
1da177e4 | 184 | udf_discard_prealloc(inode); |
2c948b3f | 185 | udf_truncate_tail_extent(inode); |
4d0fb621 | 186 | up_write(&UDF_I(inode)->i_data_sem); |
5955102c | 187 | inode_unlock(inode); |
1da177e4 LT |
188 | } |
189 | return 0; | |
190 | } | |
191 | ||
3c212048 JK |
192 | static int udf_file_mmap(struct file *file, struct vm_area_struct *vma) |
193 | { | |
194 | file_accessed(file); | |
195 | vma->vm_ops = &udf_file_vm_ops; | |
196 | ||
197 | return 0; | |
198 | } | |
199 | ||
4b6f5d20 | 200 | const struct file_operations udf_file_operations = { |
aad4f8bb | 201 | .read_iter = generic_file_read_iter, |
2f07a88b | 202 | .unlocked_ioctl = udf_ioctl, |
36350462 | 203 | .open = generic_file_open, |
3c212048 | 204 | .mmap = udf_file_mmap, |
d4637bc1 | 205 | .write_iter = udf_file_write_iter, |
28de7948 | 206 | .release = udf_release_file, |
1b061d92 | 207 | .fsync = generic_file_fsync, |
2cb1e089 | 208 | .splice_read = filemap_splice_read, |
6c78973d | 209 | .splice_write = iter_file_splice_write, |
5c89468c | 210 | .llseek = generic_file_llseek, |
1da177e4 LT |
211 | }; |
212 | ||
c1632a0f | 213 | static int udf_setattr(struct mnt_idmap *idmap, struct dentry *dentry, |
549c7297 | 214 | struct iattr *attr) |
d39aae9e | 215 | { |
2b0143b5 | 216 | struct inode *inode = d_inode(dentry); |
ecd10aa4 | 217 | struct super_block *sb = inode->i_sb; |
d39aae9e CH |
218 | int error; |
219 | ||
c1632a0f | 220 | error = setattr_prepare(&nop_mnt_idmap, dentry, attr); |
d39aae9e CH |
221 | if (error) |
222 | return error; | |
1025774c | 223 | |
ecd10aa4 JK |
224 | if ((attr->ia_valid & ATTR_UID) && |
225 | UDF_QUERY_FLAG(sb, UDF_FLAG_UID_SET) && | |
226 | !uid_eq(attr->ia_uid, UDF_SB(sb)->s_uid)) | |
227 | return -EPERM; | |
228 | if ((attr->ia_valid & ATTR_GID) && | |
229 | UDF_QUERY_FLAG(sb, UDF_FLAG_GID_SET) && | |
230 | !gid_eq(attr->ia_gid, UDF_SB(sb)->s_gid)) | |
231 | return -EPERM; | |
232 | ||
1025774c CH |
233 | if ((attr->ia_valid & ATTR_SIZE) && |
234 | attr->ia_size != i_size_read(inode)) { | |
7e49b6f2 | 235 | error = udf_setsize(inode, attr->ia_size); |
1025774c CH |
236 | if (error) |
237 | return error; | |
238 | } | |
239 | ||
c3367a1b SM |
240 | if (attr->ia_valid & ATTR_MODE) |
241 | udf_update_extra_perms(inode, attr->ia_mode); | |
242 | ||
c1632a0f | 243 | setattr_copy(&nop_mnt_idmap, inode, attr); |
1025774c CH |
244 | mark_inode_dirty(inode); |
245 | return 0; | |
d39aae9e CH |
246 | } |
247 | ||
c5ef1c42 | 248 | const struct inode_operations udf_file_inode_operations = { |
d39aae9e | 249 | .setattr = udf_setattr, |
1da177e4 | 250 | }; |