Blame view
fs/ufs/truncate.c
12.7 KB
1da177e4c
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
/* * linux/fs/ufs/truncate.c * * Copyright (C) 1998 * Daniel Pirkl <daniel.pirkl@email.cz> * Charles University, Faculty of Mathematics and Physics * * from * * linux/fs/ext2/truncate.c * * Copyright (C) 1992, 1993, 1994, 1995 * Remy Card (card@masi.ibp.fr) * Laboratoire MASI - Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * * from * * linux/fs/minix/truncate.c * * Copyright (C) 1991, 1992 Linus Torvalds * * Big-endian to little-endian byte-swapping/bitmaps by * David S. Miller (davem@caip.rutgers.edu), 1995 */ /* * Real random numbers for secure rm added 94/02/18 * Idea from Pierre del Perugia <delperug@gla.ecoledoc.ibp.fr> */ |
09114eb8c
|
31 |
/* |
54fb996ac
|
32 33 |
* Adoptation to use page cache and UFS2 write support by * Evgeniy Dushistov <dushistov@mail.ru>, 2006-2007 |
09114eb8c
|
34 |
*/ |
1da177e4c
|
35 36 |
#include <linux/errno.h> #include <linux/fs.h> |
1da177e4c
|
37 38 39 40 |
#include <linux/fcntl.h> #include <linux/time.h> #include <linux/stat.h> #include <linux/string.h> |
1da177e4c
|
41 42 43 |
#include <linux/buffer_head.h> #include <linux/blkdev.h> #include <linux/sched.h> |
e54205988
|
44 |
#include "ufs_fs.h" |
bcd6d4ecf
|
45 |
#include "ufs.h" |
1da177e4c
|
46 47 |
#include "swab.h" #include "util.h" |
1da177e4c
|
48 49 50 51 52 53 54 55 56 57 58 59 |
/* * Secure deletion currently doesn't work. It interacts very badly * with buffers shared with memory mappings, and for that reason * can't be done in the truncate() routines. It should instead be * done separately in "release()" before calling the truncate routines * that will release the actual file blocks. * * Linus */ #define DIRECT_BLOCK ((inode->i_size + uspi->s_bsize - 1) >> uspi->s_bshift) #define DIRECT_FRAGMENT ((inode->i_size + uspi->s_fsize - 1) >> uspi->s_fshift) |
1da177e4c
|
60 |
|
54fb996ac
|
61 |
static int ufs_trunc_direct(struct inode *inode) |
1da177e4c
|
62 63 64 65 |
{ struct ufs_inode_info *ufsi = UFS_I(inode); struct super_block * sb; struct ufs_sb_private_info * uspi; |
54fb996ac
|
66 67 |
void *p; u64 frag1, frag2, frag3, frag4, block1, block2; |
1da177e4c
|
68 |
unsigned frag_to_free, free_count; |
09114eb8c
|
69 |
unsigned i, tmp; |
1da177e4c
|
70 71 |
int retry; |
4b25a37e2
|
72 73 |
UFSD("ENTER: ino %lu ", inode->i_ino); |
1da177e4c
|
74 75 76 77 78 79 80 81 82 |
sb = inode->i_sb; uspi = UFS_SB(sb)->s_uspi; frag_to_free = 0; free_count = 0; retry = 0; frag1 = DIRECT_FRAGMENT; |
1d5827235
|
83 |
frag4 = min_t(u64, UFS_NDIR_FRAGMENT, ufsi->i_lastfrag); |
1da177e4c
|
84 85 86 87 88 89 |
frag2 = ((frag1 & uspi->s_fpbmask) ? ((frag1 | uspi->s_fpbmask) + 1) : frag1); frag3 = frag4 & ~uspi->s_fpbmask; block1 = block2 = 0; if (frag2 > frag3) { frag2 = frag4; frag3 = frag4 = 0; |
54fb996ac
|
90 |
} else if (frag2 < frag3) { |
1da177e4c
|
91 92 93 |
block1 = ufs_fragstoblks (frag2); block2 = ufs_fragstoblks (frag3); } |
4b25a37e2
|
94 95 96 |
UFSD("ino %lu, frag1 %llu, frag2 %llu, block1 %llu, block2 %llu," " frag3 %llu, frag4 %llu ", inode->i_ino, |
54fb996ac
|
97 98 99 |
(unsigned long long)frag1, (unsigned long long)frag2, (unsigned long long)block1, (unsigned long long)block2, (unsigned long long)frag3, (unsigned long long)frag4); |
1da177e4c
|
100 101 102 103 104 105 106 |
if (frag1 >= frag2) goto next1; /* * Free first free fragments */ |
54fb996ac
|
107 108 |
p = ufs_get_direct_data_ptr(uspi, ufsi, ufs_fragstoblks(frag1)); tmp = ufs_data_ptr_to_cpu(sb, p); |
1da177e4c
|
109 110 |
if (!tmp ) ufs_panic (sb, "ufs_trunc_direct", "internal error"); |
8682164a6
|
111 |
frag2 -= frag1; |
1da177e4c
|
112 |
frag1 = ufs_fragnum (frag1); |
09114eb8c
|
113 |
|
8682164a6
|
114 |
ufs_free_fragments(inode, tmp + frag1, frag2); |
50aa4eb0b
|
115 |
mark_inode_dirty(inode); |
1da177e4c
|
116 117 118 119 120 121 122 |
frag_to_free = tmp + frag1; next1: /* * Free whole blocks */ for (i = block1 ; i < block2; i++) { |
54fb996ac
|
123 124 |
p = ufs_get_direct_data_ptr(uspi, ufsi, i); tmp = ufs_data_ptr_to_cpu(sb, p); |
1da177e4c
|
125 126 |
if (!tmp) continue; |
54fb996ac
|
127 |
ufs_data_ptr_clear(uspi, p); |
50aa4eb0b
|
128 |
|
1da177e4c
|
129 130 131 132 133 134 135 136 137 138 |
if (free_count == 0) { frag_to_free = tmp; free_count = uspi->s_fpb; } else if (free_count > 0 && frag_to_free == tmp - free_count) free_count += uspi->s_fpb; else { ufs_free_blocks (inode, frag_to_free, free_count); frag_to_free = tmp; free_count = uspi->s_fpb; } |
50aa4eb0b
|
139 |
mark_inode_dirty(inode); |
1da177e4c
|
140 141 142 143 144 145 146 147 148 149 150 |
} if (free_count > 0) ufs_free_blocks (inode, frag_to_free, free_count); if (frag3 >= frag4) goto next3; /* * Free last free fragments */ |
54fb996ac
|
151 152 |
p = ufs_get_direct_data_ptr(uspi, ufsi, ufs_fragstoblks(frag3)); tmp = ufs_data_ptr_to_cpu(sb, p); |
1da177e4c
|
153 154 155 |
if (!tmp ) ufs_panic(sb, "ufs_truncate_direct", "internal error"); frag4 = ufs_fragnum (frag4); |
54fb996ac
|
156 |
ufs_data_ptr_clear(uspi, p); |
50aa4eb0b
|
157 |
|
1da177e4c
|
158 |
ufs_free_fragments (inode, tmp, frag4); |
50aa4eb0b
|
159 |
mark_inode_dirty(inode); |
1da177e4c
|
160 |
next3: |
4b25a37e2
|
161 162 |
UFSD("EXIT: ino %lu ", inode->i_ino); |
1da177e4c
|
163 164 |
return retry; } |
54fb996ac
|
165 |
static int ufs_trunc_indirect(struct inode *inode, u64 offset, void *p) |
1da177e4c
|
166 167 168 169 |
{ struct super_block * sb; struct ufs_sb_private_info * uspi; struct ufs_buffer_head * ind_ubh; |
54fb996ac
|
170 171 172 |
void *ind; u64 tmp, indirect_block, i, frag_to_free; unsigned free_count; |
1da177e4c
|
173 |
int retry; |
54fb996ac
|
174 175 176 177 178 |
UFSD("ENTER: ino %lu, offset %llu, p: %p ", inode->i_ino, (unsigned long long)offset, p); BUG_ON(!p); |
1da177e4c
|
179 180 181 182 183 184 185 186 |
sb = inode->i_sb; uspi = UFS_SB(sb)->s_uspi; frag_to_free = 0; free_count = 0; retry = 0; |
54fb996ac
|
187 |
tmp = ufs_data_ptr_to_cpu(sb, p); |
1da177e4c
|
188 189 190 |
if (!tmp) return 0; ind_ubh = ubh_bread(sb, tmp, uspi->s_bsize); |
54fb996ac
|
191 |
if (tmp != ufs_data_ptr_to_cpu(sb, p)) { |
1da177e4c
|
192 193 194 195 |
ubh_brelse (ind_ubh); return 1; } if (!ind_ubh) { |
54fb996ac
|
196 |
ufs_data_ptr_clear(uspi, p); |
1da177e4c
|
197 198 199 200 201 |
return 0; } indirect_block = (DIRECT_BLOCK > offset) ? (DIRECT_BLOCK - offset) : 0; for (i = indirect_block; i < uspi->s_apb; i++) { |
54fb996ac
|
202 203 |
ind = ubh_get_data_ptr(uspi, ind_ubh, i); tmp = ufs_data_ptr_to_cpu(sb, ind); |
1da177e4c
|
204 205 |
if (!tmp) continue; |
09114eb8c
|
206 |
|
54fb996ac
|
207 |
ufs_data_ptr_clear(uspi, ind); |
1da177e4c
|
208 209 210 211 212 213 214 215 216 217 218 |
ubh_mark_buffer_dirty(ind_ubh); if (free_count == 0) { frag_to_free = tmp; free_count = uspi->s_fpb; } else if (free_count > 0 && frag_to_free == tmp - free_count) free_count += uspi->s_fpb; else { ufs_free_blocks (inode, frag_to_free, free_count); frag_to_free = tmp; free_count = uspi->s_fpb; } |
50aa4eb0b
|
219 |
|
1da177e4c
|
220 |
mark_inode_dirty(inode); |
1da177e4c
|
221 222 223 224 225 226 |
} if (free_count > 0) { ufs_free_blocks (inode, frag_to_free, free_count); } for (i = 0; i < uspi->s_apb; i++) |
54fb996ac
|
227 228 |
if (!ufs_is_data_ptr_zero(uspi, ubh_get_data_ptr(uspi, ind_ubh, i))) |
1da177e4c
|
229 230 |
break; if (i >= uspi->s_apb) { |
54fb996ac
|
231 232 |
tmp = ufs_data_ptr_to_cpu(sb, p); ufs_data_ptr_clear(uspi, p); |
50aa4eb0b
|
233 |
|
2061df0f8
|
234 |
ufs_free_blocks (inode, tmp, uspi->s_fpb); |
50aa4eb0b
|
235 |
mark_inode_dirty(inode); |
2061df0f8
|
236 237 |
ubh_bforget(ind_ubh); ind_ubh = NULL; |
1da177e4c
|
238 |
} |
9cb569d60
|
239 240 |
if (IS_SYNC(inode) && ind_ubh && ubh_buffer_dirty(ind_ubh)) ubh_sync_block(ind_ubh); |
1da177e4c
|
241 242 |
ubh_brelse (ind_ubh); |
4b25a37e2
|
243 244 |
UFSD("EXIT: ino %lu ", inode->i_ino); |
1da177e4c
|
245 246 247 |
return retry; } |
54fb996ac
|
248 |
static int ufs_trunc_dindirect(struct inode *inode, u64 offset, void *p) |
1da177e4c
|
249 250 251 |
{ struct super_block * sb; struct ufs_sb_private_info * uspi; |
54fb996ac
|
252 253 254 |
struct ufs_buffer_head *dind_bh; u64 i, tmp, dindirect_block; void *dind; |
1da177e4c
|
255 256 |
int retry = 0; |
4b25a37e2
|
257 258 |
UFSD("ENTER: ino %lu ", inode->i_ino); |
1da177e4c
|
259 260 261 262 263 264 265 266 |
sb = inode->i_sb; uspi = UFS_SB(sb)->s_uspi; dindirect_block = (DIRECT_BLOCK > offset) ? ((DIRECT_BLOCK - offset) >> uspi->s_apbshift) : 0; retry = 0; |
54fb996ac
|
267 |
tmp = ufs_data_ptr_to_cpu(sb, p); |
1da177e4c
|
268 269 270 |
if (!tmp) return 0; dind_bh = ubh_bread(sb, tmp, uspi->s_bsize); |
54fb996ac
|
271 |
if (tmp != ufs_data_ptr_to_cpu(sb, p)) { |
1da177e4c
|
272 273 274 275 |
ubh_brelse (dind_bh); return 1; } if (!dind_bh) { |
54fb996ac
|
276 |
ufs_data_ptr_clear(uspi, p); |
1da177e4c
|
277 278 279 280 |
return 0; } for (i = dindirect_block ; i < uspi->s_apb ; i++) { |
54fb996ac
|
281 282 |
dind = ubh_get_data_ptr(uspi, dind_bh, i); tmp = ufs_data_ptr_to_cpu(sb, dind); |
1da177e4c
|
283 284 285 286 287 288 289 |
if (!tmp) continue; retry |= ufs_trunc_indirect (inode, offset + (i << uspi->s_apbshift), dind); ubh_mark_buffer_dirty(dind_bh); } for (i = 0; i < uspi->s_apb; i++) |
54fb996ac
|
290 291 |
if (!ufs_is_data_ptr_zero(uspi, ubh_get_data_ptr(uspi, dind_bh, i))) |
1da177e4c
|
292 293 |
break; if (i >= uspi->s_apb) { |
54fb996ac
|
294 295 |
tmp = ufs_data_ptr_to_cpu(sb, p); ufs_data_ptr_clear(uspi, p); |
50aa4eb0b
|
296 297 |
ufs_free_blocks(inode, tmp, uspi->s_fpb); |
2061df0f8
|
298 |
mark_inode_dirty(inode); |
2061df0f8
|
299 300 |
ubh_bforget(dind_bh); dind_bh = NULL; |
1da177e4c
|
301 |
} |
9cb569d60
|
302 303 |
if (IS_SYNC(inode) && dind_bh && ubh_buffer_dirty(dind_bh)) ubh_sync_block(dind_bh); |
1da177e4c
|
304 305 |
ubh_brelse (dind_bh); |
4b25a37e2
|
306 307 |
UFSD("EXIT: ino %lu ", inode->i_ino); |
1da177e4c
|
308 309 310 |
return retry; } |
54fb996ac
|
311 |
static int ufs_trunc_tindirect(struct inode *inode) |
1da177e4c
|
312 |
{ |
54fb996ac
|
313 314 |
struct super_block *sb = inode->i_sb; struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi; |
1da177e4c
|
315 |
struct ufs_inode_info *ufsi = UFS_I(inode); |
1da177e4c
|
316 |
struct ufs_buffer_head * tind_bh; |
54fb996ac
|
317 318 |
u64 tindirect_block, tmp, i; void *tind, *p; |
1da177e4c
|
319 320 |
int retry; |
4b25a37e2
|
321 322 |
UFSD("ENTER: ino %lu ", inode->i_ino); |
1da177e4c
|
323 |
|
1da177e4c
|
324 325 326 327 |
retry = 0; tindirect_block = (DIRECT_BLOCK > (UFS_NDADDR + uspi->s_apb + uspi->s_2apb)) ? ((DIRECT_BLOCK - UFS_NDADDR - uspi->s_apb - uspi->s_2apb) >> uspi->s_2apbshift) : 0; |
54fb996ac
|
328 329 330 |
p = ufs_get_direct_data_ptr(uspi, ufsi, UFS_TIND_BLOCK); if (!(tmp = ufs_data_ptr_to_cpu(sb, p))) |
1da177e4c
|
331 332 |
return 0; tind_bh = ubh_bread (sb, tmp, uspi->s_bsize); |
54fb996ac
|
333 |
if (tmp != ufs_data_ptr_to_cpu(sb, p)) { |
1da177e4c
|
334 335 336 337 |
ubh_brelse (tind_bh); return 1; } if (!tind_bh) { |
54fb996ac
|
338 |
ufs_data_ptr_clear(uspi, p); |
1da177e4c
|
339 340 341 342 |
return 0; } for (i = tindirect_block ; i < uspi->s_apb ; i++) { |
0465fc0a1
|
343 |
tind = ubh_get_data_ptr(uspi, tind_bh, i); |
1da177e4c
|
344 345 346 347 348 |
retry |= ufs_trunc_dindirect(inode, UFS_NDADDR + uspi->s_apb + ((i + 1) << uspi->s_2apbshift), tind); ubh_mark_buffer_dirty(tind_bh); } for (i = 0; i < uspi->s_apb; i++) |
54fb996ac
|
349 350 |
if (!ufs_is_data_ptr_zero(uspi, ubh_get_data_ptr(uspi, tind_bh, i))) |
1da177e4c
|
351 352 |
break; if (i >= uspi->s_apb) { |
54fb996ac
|
353 354 |
tmp = ufs_data_ptr_to_cpu(sb, p); ufs_data_ptr_clear(uspi, p); |
50aa4eb0b
|
355 356 |
ufs_free_blocks(inode, tmp, uspi->s_fpb); |
2061df0f8
|
357 |
mark_inode_dirty(inode); |
2061df0f8
|
358 359 |
ubh_bforget(tind_bh); tind_bh = NULL; |
1da177e4c
|
360 |
} |
9cb569d60
|
361 362 |
if (IS_SYNC(inode) && tind_bh && ubh_buffer_dirty(tind_bh)) ubh_sync_block(tind_bh); |
1da177e4c
|
363 364 |
ubh_brelse (tind_bh); |
4b25a37e2
|
365 366 |
UFSD("EXIT: ino %lu ", inode->i_ino); |
1da177e4c
|
367 368 |
return retry; } |
10e5dce07
|
369 370 |
static int ufs_alloc_lastblock(struct inode *inode) |
1da177e4c
|
371 |
{ |
10e5dce07
|
372 |
int err = 0; |
4b25a37e2
|
373 |
struct super_block *sb = inode->i_sb; |
10e5dce07
|
374 |
struct address_space *mapping = inode->i_mapping; |
4b25a37e2
|
375 |
struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi; |
54fb996ac
|
376 377 |
unsigned i, end; sector_t lastfrag; |
10e5dce07
|
378 379 |
struct page *lastpage; struct buffer_head *bh; |
4b25a37e2
|
380 |
u64 phys64; |
10e5dce07
|
381 382 |
lastfrag = (i_size_read(inode) + uspi->s_fsize - 1) >> uspi->s_fshift; |
ecdc63948
|
383 |
if (!lastfrag) |
10e5dce07
|
384 |
goto out; |
ecdc63948
|
385 |
|
10e5dce07
|
386 387 388 389 390 391 392 393 394 395 396 397 398 |
lastfrag--; lastpage = ufs_get_locked_page(mapping, lastfrag >> (PAGE_CACHE_SHIFT - inode->i_blkbits)); if (IS_ERR(lastpage)) { err = -EIO; goto out; } end = lastfrag & ((1 << (PAGE_CACHE_SHIFT - inode->i_blkbits)) - 1); bh = page_buffers(lastpage); for (i = 0; i < end; ++i) bh = bh->b_this_page; |
ecdc63948
|
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 |
err = ufs_getfrag_block(inode, lastfrag, bh, 1); if (unlikely(err)) goto out_unlock; if (buffer_new(bh)) { clear_buffer_new(bh); unmap_underlying_metadata(bh->b_bdev, bh->b_blocknr); /* * we do not zeroize fragment, because of * if it maped to hole, it already contains zeroes */ set_buffer_uptodate(bh); mark_buffer_dirty(bh); set_page_dirty(lastpage); |
10e5dce07
|
416 |
} |
ecdc63948
|
417 |
|
4b25a37e2
|
418 419 420 421 422 423 424 425 426 427 428 429 430 431 |
if (lastfrag >= UFS_IND_FRAGMENT) { end = uspi->s_fpb - ufs_fragnum(lastfrag) - 1; phys64 = bh->b_blocknr + 1; for (i = 0; i < end; ++i) { bh = sb_getblk(sb, i + phys64); lock_buffer(bh); memset(bh->b_data, 0, sb->s_blocksize); set_buffer_uptodate(bh); mark_buffer_dirty(bh); unlock_buffer(bh); sync_dirty_buffer(bh); brelse(bh); } } |
10e5dce07
|
432 433 434 435 436 437 438 439 440 441 442 443 |
out_unlock: ufs_put_locked_page(lastpage); out: return err; } int ufs_truncate(struct inode *inode, loff_t old_i_size) { struct ufs_inode_info *ufsi = UFS_I(inode); struct super_block *sb = inode->i_sb; struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi; int retry, err = 0; |
1da177e4c
|
444 |
|
54fb996ac
|
445 446 447 448 |
UFSD("ENTER: ino %lu, i_size: %llu, old_i_size: %llu ", inode->i_ino, (unsigned long long)i_size_read(inode), (unsigned long long)old_i_size); |
1da177e4c
|
449 |
|
10e5dce07
|
450 451 452 |
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return -EINVAL; |
1da177e4c
|
453 |
if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) |
10e5dce07
|
454 |
return -EPERM; |
ecdc63948
|
455 |
err = ufs_alloc_lastblock(inode); |
10e5dce07
|
456 |
|
ecdc63948
|
457 458 459 |
if (err) { i_size_write(inode, old_i_size); goto out; |
10e5dce07
|
460 |
} |
09114eb8c
|
461 |
|
10e5dce07
|
462 |
block_truncate_page(inode->i_mapping, inode->i_size, ufs_getfrag_block); |
09114eb8c
|
463 |
|
1da177e4c
|
464 465 |
while (1) { retry = ufs_trunc_direct(inode); |
54fb996ac
|
466 467 468 469 470 471 |
retry |= ufs_trunc_indirect(inode, UFS_IND_BLOCK, ufs_get_direct_data_ptr(uspi, ufsi, UFS_IND_BLOCK)); retry |= ufs_trunc_dindirect(inode, UFS_IND_BLOCK + uspi->s_apb, ufs_get_direct_data_ptr(uspi, ufsi, UFS_DIND_BLOCK)); |
1da177e4c
|
472 473 474 475 476 |
retry |= ufs_trunc_tindirect (inode); if (!retry) break; if (IS_SYNC(inode) && (inode->i_state & I_DIRTY)) ufs_sync_inode (inode); |
1da177e4c
|
477 478 |
yield(); } |
09114eb8c
|
479 |
|
1da177e4c
|
480 |
inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; |
ecdc63948
|
481 |
ufsi->i_lastfrag = DIRECT_FRAGMENT; |
1da177e4c
|
482 |
mark_inode_dirty(inode); |
10e5dce07
|
483 484 485 486 |
out: UFSD("EXIT: err %d ", err); return err; |
1da177e4c
|
487 |
} |
10e5dce07
|
488 |
|
311b9549e
|
489 |
int ufs_setattr(struct dentry *dentry, struct iattr *attr) |
10e5dce07
|
490 491 492 493 494 495 496 497 |
{ struct inode *inode = dentry->d_inode; unsigned int ia_valid = attr->ia_valid; int error; error = inode_change_ok(inode, attr); if (error) return error; |
12755627b
|
498 |
if (ia_valid & ATTR_SIZE && attr->ia_size != inode->i_size) { |
10e5dce07
|
499 |
loff_t old_i_size = inode->i_size; |
907f4554e
|
500 |
|
2c27c65ed
|
501 502 |
/* XXX(truncate): truncate_setsize should be called last */ truncate_setsize(inode, attr->ia_size); |
788257d61
|
503 |
lock_ufs(inode->i_sb); |
10e5dce07
|
504 |
error = ufs_truncate(inode, old_i_size); |
788257d61
|
505 |
unlock_ufs(inode->i_sb); |
10e5dce07
|
506 507 508 |
if (error) return error; } |
1025774ce
|
509 510 511 512 |
setattr_copy(inode, attr); mark_inode_dirty(inode); return 0; |
10e5dce07
|
513 |
} |
c5ef1c42c
|
514 |
const struct inode_operations ufs_file_inode_operations = { |
10e5dce07
|
515 516 |
.setattr = ufs_setattr, }; |