aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ntfs
diff options
context:
space:
mode:
authorAnton Altaparmakov <aia21@cantab.net>2005-03-09 10:15:06 -0500
committerAnton Altaparmakov <aia21@cantab.net>2005-05-05 06:22:07 -0400
commit2bfb4fff3e9731ecfe745881e53cfb2e646c47bb (patch)
treea607df8a7d0532803584dab19bf13b69acbb668d /fs/ntfs
parentc0c1cc0e46b36347f11b566f99087dc5e6fc1b89 (diff)
NTFS: Add fs/ntfs/attrib.[hc]::ntfs_attr_make_non_resident().
Signed-off-by: Anton Altaparmakov <aia21@cantab.net>
Diffstat (limited to 'fs/ntfs')
-rw-r--r--fs/ntfs/ChangeLog1
-rw-r--r--fs/ntfs/attrib.c300
-rw-r--r--fs/ntfs/attrib.h2
3 files changed, 303 insertions, 0 deletions
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
index 7507a56a4921..71680a92b4d6 100644
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -97,6 +97,7 @@ ToDo/Notes:
97 whether someone else did not already do the work we wanted to do. 97 whether someone else did not already do the work we wanted to do.
98 - Rename fs/ntfs/attrib.c::ntfs_find_vcn_nolock() to 98 - Rename fs/ntfs/attrib.c::ntfs_find_vcn_nolock() to
99 ntfs_attr_find_vcn_nolock() and update all callers. 99 ntfs_attr_find_vcn_nolock() and update all callers.
100 - Add fs/ntfs/attrib.[hc]::ntfs_attr_make_non_resident().
100 101
1012.1.22 - Many bug and race fixes and error handling improvements. 1022.1.22 - Many bug and race fixes and error handling improvements.
102 103
diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c
index 6de5e04e97a2..3b9de4040216 100644
--- a/fs/ntfs/attrib.c
+++ b/fs/ntfs/attrib.c
@@ -25,6 +25,8 @@
25#include "attrib.h" 25#include "attrib.h"
26#include "debug.h" 26#include "debug.h"
27#include "layout.h" 27#include "layout.h"
28#include "lcnalloc.h"
29#include "malloc.h"
28#include "mft.h" 30#include "mft.h"
29#include "ntfs.h" 31#include "ntfs.h"
30#include "types.h" 32#include "types.h"
@@ -1227,6 +1229,304 @@ int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size)
1227} 1229}
1228 1230
1229/** 1231/**
1232 * ntfs_attr_make_non_resident - convert a resident to a non-resident attribute
1233 * @ni: ntfs inode describing the attribute to convert
1234 *
1235 * Convert the resident ntfs attribute described by the ntfs inode @ni to a
1236 * non-resident one.
1237 *
1238 * Return 0 on success and -errno on error. The following error return codes
1239 * are defined:
1240 * -EPERM - The attribute is not allowed to be non-resident.
1241 * -ENOMEM - Not enough memory.
1242 * -ENOSPC - Not enough disk space.
1243 * -EINVAL - Attribute not defined on the volume.
1244 * -EIO - I/o error or other error.
1245 *
1246 * NOTE to self: No changes in the attribute list are required to move from
1247 * a resident to a non-resident attribute.
1248 *
1249 * Locking: - The caller must hold i_sem on the inode.
1250 */
1251int ntfs_attr_make_non_resident(ntfs_inode *ni)
1252{
1253 s64 new_size;
1254 struct inode *vi = VFS_I(ni);
1255 ntfs_volume *vol = ni->vol;
1256 ntfs_inode *base_ni;
1257 MFT_RECORD *m;
1258 ATTR_RECORD *a;
1259 ntfs_attr_search_ctx *ctx;
1260 struct page *page;
1261 runlist_element *rl;
1262 u8 *kaddr;
1263 unsigned long flags;
1264 int mp_size, mp_ofs, name_ofs, arec_size, err, err2;
1265 u32 attr_size;
1266 u8 old_res_attr_flags;
1267
1268 /* Check that the attribute is allowed to be non-resident. */
1269 err = ntfs_attr_can_be_non_resident(vol, ni->type);
1270 if (unlikely(err)) {
1271 if (err == -EPERM)
1272 ntfs_debug("Attribute is not allowed to be "
1273 "non-resident.");
1274 else
1275 ntfs_debug("Attribute not defined on the NTFS "
1276 "volume!");
1277 return err;
1278 }
1279 /*
1280 * The size needs to be aligned to a cluster boundary for allocation
1281 * purposes.
1282 */
1283 new_size = (i_size_read(vi) + vol->cluster_size - 1) &
1284 ~(vol->cluster_size - 1);
1285 if (new_size > 0) {
1286 /*
1287 * Will need the page later and since the page lock nests
1288 * outside all ntfs locks, we need to get the page now.
1289 */
1290 page = find_or_create_page(vi->i_mapping, 0,
1291 mapping_gfp_mask(vi->i_mapping));
1292 if (unlikely(!page))
1293 return -ENOMEM;
1294 /* Start by allocating clusters to hold the attribute value. */
1295 rl = ntfs_cluster_alloc(vol, 0, new_size >>
1296 vol->cluster_size_bits, -1, DATA_ZONE);
1297 if (IS_ERR(rl)) {
1298 err = PTR_ERR(rl);
1299 ntfs_debug("Failed to allocate cluster%s, error code "
1300 "%i.\n", (new_size >>
1301 vol->cluster_size_bits) > 1 ? "s" : "",
1302 err);
1303 goto page_err_out;
1304 }
1305 } else {
1306 rl = NULL;
1307 page = NULL;
1308 }
1309 /* Determine the size of the mapping pairs array. */
1310 mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, 0);
1311 if (unlikely(mp_size < 0)) {
1312 err = mp_size;
1313 ntfs_debug("Failed to get size for mapping pairs array, error "
1314 "code %i.", err);
1315 goto rl_err_out;
1316 }
1317 down_write(&ni->runlist.lock);
1318 if (!NInoAttr(ni))
1319 base_ni = ni;
1320 else
1321 base_ni = ni->ext.base_ntfs_ino;
1322 m = map_mft_record(base_ni);
1323 if (IS_ERR(m)) {
1324 err = PTR_ERR(m);
1325 m = NULL;
1326 ctx = NULL;
1327 goto err_out;
1328 }
1329 ctx = ntfs_attr_get_search_ctx(base_ni, m);
1330 if (unlikely(!ctx)) {
1331 err = -ENOMEM;
1332 goto err_out;
1333 }
1334 err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
1335 CASE_SENSITIVE, 0, NULL, 0, ctx);
1336 if (unlikely(err)) {
1337 if (err == -ENOENT)
1338 err = -EIO;
1339 goto err_out;
1340 }
1341 m = ctx->mrec;
1342 a = ctx->attr;
1343 BUG_ON(NInoNonResident(ni));
1344 BUG_ON(a->non_resident);
1345 /*
1346 * Calculate new offsets for the name and the mapping pairs array.
1347 * We assume the attribute is not compressed or sparse.
1348 */
1349 name_ofs = (offsetof(ATTR_REC,
1350 data.non_resident.compressed_size) + 7) & ~7;
1351 mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7;
1352 /*
1353 * Determine the size of the resident part of the now non-resident
1354 * attribute record.
1355 */
1356 arec_size = (mp_ofs + mp_size + 7) & ~7;
1357 /*
1358 * If the page is not uptodate bring it uptodate by copying from the
1359 * attribute value.
1360 */
1361 attr_size = le32_to_cpu(a->data.resident.value_length);
1362 BUG_ON(attr_size != i_size_read(vi));
1363 if (page && !PageUptodate(page)) {
1364 kaddr = kmap_atomic(page, KM_USER0);
1365 memcpy(kaddr, (u8*)a +
1366 le16_to_cpu(a->data.resident.value_offset),
1367 attr_size);
1368 memset(kaddr + attr_size, 0, PAGE_CACHE_SIZE - attr_size);
1369 kunmap_atomic(kaddr, KM_USER0);
1370 flush_dcache_page(page);
1371 SetPageUptodate(page);
1372 }
1373 /* Backup the attribute flag. */
1374 old_res_attr_flags = a->data.resident.flags;
1375 /* Resize the resident part of the attribute record. */
1376 err = ntfs_attr_record_resize(m, a, arec_size);
1377 if (unlikely(err))
1378 goto err_out;
1379 /* Setup the in-memory attribute structure to be non-resident. */
1380 NInoSetNonResident(ni);
1381 ni->runlist.rl = rl;
1382 write_lock_irqsave(&ni->size_lock, flags);
1383 ni->allocated_size = new_size;
1384 write_unlock_irqrestore(&ni->size_lock, flags);
1385 /*
1386 * FIXME: For now just clear all of these as we do not support them
1387 * when writing.
1388 */
1389 NInoClearCompressed(ni);
1390 NInoClearSparse(ni);
1391 NInoClearEncrypted(ni);
1392 /*
1393 * Convert the resident part of the attribute record to describe a
1394 * non-resident attribute.
1395 */
1396 a->non_resident = 1;
1397 /* Move the attribute name if it exists and update the offset. */
1398 if (a->name_length)
1399 memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset),
1400 a->name_length * sizeof(ntfschar));
1401 a->name_offset = cpu_to_le16(name_ofs);
1402 /* Update the flags to match the in-memory ones. */
1403 a->flags &= cpu_to_le16(0xffff & ~le16_to_cpu(ATTR_IS_SPARSE |
1404 ATTR_IS_ENCRYPTED | ATTR_COMPRESSION_MASK));
1405 /* Setup the fields specific to non-resident attributes. */
1406 a->data.non_resident.lowest_vcn = 0;
1407 a->data.non_resident.highest_vcn = cpu_to_sle64((new_size - 1) >>
1408 vol->cluster_size_bits);
1409 a->data.non_resident.mapping_pairs_offset = cpu_to_le16(mp_ofs);
1410 a->data.non_resident.compression_unit = 0;
1411 memset(&a->data.non_resident.reserved, 0,
1412 sizeof(a->data.non_resident.reserved));
1413 a->data.non_resident.allocated_size = cpu_to_sle64(new_size);
1414 a->data.non_resident.data_size =
1415 a->data.non_resident.initialized_size =
1416 cpu_to_sle64(attr_size);
1417 /* Generate the mapping pairs array into the attribute record. */
1418 err = ntfs_mapping_pairs_build(vol, (u8*)a + mp_ofs,
1419 arec_size - mp_ofs, rl, 0, NULL);
1420 if (unlikely(err)) {
1421 ntfs_debug("Failed to build mapping pairs, error code %i.",
1422 err);
1423 goto undo_err_out;
1424 }
1425 /* Mark the mft record dirty, so it gets written back. */
1426 flush_dcache_mft_record_page(ctx->ntfs_ino);
1427 mark_mft_record_dirty(ctx->ntfs_ino);
1428 ntfs_attr_put_search_ctx(ctx);
1429 unmap_mft_record(base_ni);
1430 up_write(&ni->runlist.lock);
1431 if (page) {
1432 set_page_dirty(page);
1433 unlock_page(page);
1434 page_cache_release(page);
1435 }
1436 ntfs_debug("Done.");
1437 return 0;
1438undo_err_out:
1439 /* Convert the attribute back into a resident attribute. */
1440 a->non_resident = 0;
1441 /* Move the attribute name if it exists and update the offset. */
1442 name_ofs = (offsetof(ATTR_RECORD, data.resident.reserved) +
1443 sizeof(a->data.resident.reserved) + 7) & ~7;
1444 if (a->name_length)
1445 memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset),
1446 a->name_length * sizeof(ntfschar));
1447 mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7;
1448 a->name_offset = cpu_to_le16(name_ofs);
1449 arec_size = (mp_ofs + attr_size + 7) & ~7;
1450 /* Resize the resident part of the attribute record. */
1451 err2 = ntfs_attr_record_resize(m, a, arec_size);
1452 if (unlikely(err2)) {
1453 /*
1454 * This cannot happen (well if memory corruption is at work it
1455 * could happen in theory), but deal with it as well as we can.
1456 * If the old size is too small, truncate the attribute,
1457 * otherwise simply give it a larger allocated size.
1458 * FIXME: Should check whether chkdsk complains when the
1459 * allocated size is much bigger than the resident value size.
1460 */
1461 arec_size = le32_to_cpu(a->length);
1462 if ((mp_ofs + attr_size) > arec_size) {
1463 err2 = attr_size;
1464 attr_size = arec_size - mp_ofs;
1465 ntfs_error(vol->sb, "Failed to undo partial resident "
1466 "to non-resident attribute "
1467 "conversion. Truncating inode 0x%lx, "
1468 "attribute type 0x%x from %i bytes to "
1469 "%i bytes to maintain metadata "
1470 "consistency. THIS MEANS YOU ARE "
1471 "LOSING %i BYTES DATA FROM THIS %s.",
1472 vi->i_ino,
1473 (unsigned)le32_to_cpu(ni->type),
1474 err2, attr_size, err2 - attr_size,
1475 ((ni->type == AT_DATA) &&
1476 !ni->name_len) ? "FILE": "ATTRIBUTE");
1477 write_lock_irqsave(&ni->size_lock, flags);
1478 ni->initialized_size = attr_size;
1479 i_size_write(vi, attr_size);
1480 write_unlock_irqrestore(&ni->size_lock, flags);
1481 }
1482 }
1483 /* Setup the fields specific to resident attributes. */
1484 a->data.resident.value_length = cpu_to_le32(attr_size);
1485 a->data.resident.value_offset = cpu_to_le16(mp_ofs);
1486 a->data.resident.flags = old_res_attr_flags;
1487 memset(&a->data.resident.reserved, 0,
1488 sizeof(a->data.resident.reserved));
1489 /* Copy the data from the page back to the attribute value. */
1490 if (page) {
1491 kaddr = kmap_atomic(page, KM_USER0);
1492 memcpy((u8*)a + mp_ofs, kaddr, attr_size);
1493 kunmap_atomic(kaddr, KM_USER0);
1494 }
1495 /* Finally setup the ntfs inode appropriately. */
1496 write_lock_irqsave(&ni->size_lock, flags);
1497 ni->allocated_size = arec_size - mp_ofs;
1498 write_unlock_irqrestore(&ni->size_lock, flags);
1499 NInoClearNonResident(ni);
1500 /* Mark the mft record dirty, so it gets written back. */
1501 flush_dcache_mft_record_page(ctx->ntfs_ino);
1502 mark_mft_record_dirty(ctx->ntfs_ino);
1503err_out:
1504 if (ctx)
1505 ntfs_attr_put_search_ctx(ctx);
1506 if (m)
1507 unmap_mft_record(base_ni);
1508 ni->runlist.rl = NULL;
1509 up_write(&ni->runlist.lock);
1510rl_err_out:
1511 if (rl) {
1512 if (ntfs_cluster_free_from_rl(vol, rl) < 0) {
1513 ntfs_free(rl);
1514 ntfs_error(vol->sb, "Failed to release allocated "
1515 "cluster(s) in error code path. Run "
1516 "chkdsk to recover the lost "
1517 "cluster(s).");
1518 NVolSetErrors(vol);
1519 }
1520page_err_out:
1521 unlock_page(page);
1522 page_cache_release(page);
1523 }
1524 if (err == -EINVAL)
1525 err = -EIO;
1526 return err;
1527}
1528
1529/**
1230 * ntfs_attr_set - fill (a part of) an attribute with a byte 1530 * ntfs_attr_set - fill (a part of) an attribute with a byte
1231 * @ni: ntfs inode describing the attribute to fill 1531 * @ni: ntfs inode describing the attribute to fill
1232 * @ofs: offset inside the attribute at which to start to fill 1532 * @ofs: offset inside the attribute at which to start to fill
diff --git a/fs/ntfs/attrib.h b/fs/ntfs/attrib.h
index e0a50f1ca76f..d73385198336 100644
--- a/fs/ntfs/attrib.h
+++ b/fs/ntfs/attrib.h
@@ -98,6 +98,8 @@ extern int ntfs_attr_can_be_resident(const ntfs_volume *vol,
98 98
99extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size); 99extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size);
100 100
101extern int ntfs_attr_make_non_resident(ntfs_inode *ni);
102
101extern int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt, 103extern int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt,
102 const u8 val); 104 const u8 val);
103 105