Commit f86096b8 authored by Linus Torvalds's avatar Linus Torvalds

Merge http://linux-ntfs.bkbits.net/ntfs-2.5

into home.transmeta.com:/home/torvalds/v2.5/linux
parents ad39cbdf 522c6443
...@@ -247,6 +247,19 @@ ChangeLog ...@@ -247,6 +247,19 @@ ChangeLog
Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog. Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog.
2.1.4:
- Minor update allowing compilation with all gcc versions (well, the
ones the kernel can be compiled with anyway).
2.1.3:
- Major bug fixes for reading files and volumes in corner cases which
were being hit by Windows 2k/XP users.
2.1.2:
- Major bug fixes aleviating the hangs in statfs experienced by some
users.
2.1.1:
- Update handling of compressed files so people no longer get the
frequently reported warning messages about initialized_size !=
data_size.
2.1.0: 2.1.0:
- Add configuration option for developmental write support. - Add configuration option for developmental write support.
- Initial implementation of file overwriting. (Writes to resident files - Initial implementation of file overwriting. (Writes to resident files
......
...@@ -20,6 +20,31 @@ ToDo: ...@@ -20,6 +20,31 @@ ToDo:
sufficient for synchronisation here. We then just need to make sure sufficient for synchronisation here. We then just need to make sure
ntfs_readpage/writepage/truncate interoperate properly with us. ntfs_readpage/writepage/truncate interoperate properly with us.
2.1.4 - Reduce compiler requirements.
- Remove all uses of unnamed structs and unions in the driver to make
old and newer gcc versions happy. Makes it a bit uglier IMO but at
least people will stop hassling me about it.
2.1.3 - Important bug fixes in corner cases.
- super.c::parse_ntfs_boot_sector(): Correct the check for 64-bit
clusters. (Philipp Thomas)
- attrib.c::load_attribute_list(): Fix bug when initialized_size is a
multiple of the block_size but not the cluster size. (Szabolcs
Szakacsits <szaka@sienet.hu>)
2.1.2 - Important bug fixes aleviating the hangs in statfs.
- Fix buggy free cluster and free inode determination logic.
2.1.1 - Minor updates.
- Add handling for initialized_size != data_size in compressed files.
- Reduce function local stack usage from 0x3d4 bytes to just noise in
fs/ntfs/upcase.c. (Randy Dunlap <rddunlap@osdl.ord>)
- Remove compiler warnings for newer gcc.
2.1.0 - First steps towards write support: implement file overwrite. 2.1.0 - First steps towards write support: implement file overwrite.
- Add configuration option for developmental write support with an - Add configuration option for developmental write support with an
......
...@@ -5,7 +5,7 @@ obj-$(CONFIG_NTFS_FS) += ntfs.o ...@@ -5,7 +5,7 @@ obj-$(CONFIG_NTFS_FS) += ntfs.o
ntfs-objs := aops.o attrib.o compress.o debug.o dir.o file.o inode.o mft.o \ ntfs-objs := aops.o attrib.o compress.o debug.o dir.o file.o inode.o mft.o \
mst.o namei.o super.o sysctl.o time.o unistr.o upcase.o mst.o namei.o super.o sysctl.o time.o unistr.o upcase.o
EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.0\" EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.4\"
ifeq ($(CONFIG_NTFS_DEBUG),y) ifeq ($(CONFIG_NTFS_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG EXTRA_CFLAGS += -DDEBUG
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
* aops.c - NTFS kernel address space operations and page cache handling. * aops.c - NTFS kernel address space operations and page cache handling.
* Part of the Linux-NTFS project. * Part of the Linux-NTFS project.
* *
* Copyright (c) 2001,2002 Anton Altaparmakov. * Copyright (c) 2001-2003 Anton Altaparmakov
* Copyright (c) 2002 Richard Russon. * Copyright (c) 2002 Richard Russon
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published * modify it under the terms of the GNU General Public License as published
...@@ -111,7 +111,7 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate) ...@@ -111,7 +111,7 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate)
unsigned int i, recs, nr_err; unsigned int i, recs, nr_err;
u32 rec_size; u32 rec_size;
rec_size = ni->_IDM(index_block_size); rec_size = ni->itype.index.block_size;
recs = PAGE_CACHE_SIZE / rec_size; recs = PAGE_CACHE_SIZE / rec_size;
addr = kmap_atomic(page, KM_BIO_SRC_IRQ); addr = kmap_atomic(page, KM_BIO_SRC_IRQ);
for (i = nr_err = 0; i < recs; i++) { for (i = nr_err = 0; i < recs; i++) {
...@@ -124,7 +124,7 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate) ...@@ -124,7 +124,7 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate)
ni->mft_no ? "index" : "mft", ni->mft_no ? "index" : "mft",
(long long)(((s64)page->index << (long long)(((s64)page->index <<
PAGE_CACHE_SHIFT >> PAGE_CACHE_SHIFT >>
ni->_IDM(index_block_size_bits)) + i)); ni->itype.index.block_size_bits) + i));
} }
flush_dcache_page(page); flush_dcache_page(page);
kunmap_atomic(addr, KM_BIO_SRC_IRQ); kunmap_atomic(addr, KM_BIO_SRC_IRQ);
...@@ -383,7 +383,7 @@ int ntfs_readpage(struct file *file, struct page *page) ...@@ -383,7 +383,7 @@ int ntfs_readpage(struct file *file, struct page *page)
if (!NInoAttr(ni)) if (!NInoAttr(ni))
base_ni = ni; base_ni = ni;
else else
base_ni = ni->_INE(base_ntfs_ino); base_ni = ni->ext.base_ntfs_ino;
/* Map, pin, and lock the mft record. */ /* Map, pin, and lock the mft record. */
mrec = map_mft_record(base_ni); mrec = map_mft_record(base_ni);
...@@ -406,7 +406,7 @@ int ntfs_readpage(struct file *file, struct page *page) ...@@ -406,7 +406,7 @@ int ntfs_readpage(struct file *file, struct page *page)
attr_pos = page->index << PAGE_CACHE_SHIFT; attr_pos = page->index << PAGE_CACHE_SHIFT;
/* The total length of the attribute value. */ /* The total length of the attribute value. */
attr_len = le32_to_cpu(ctx->attr->_ARA(value_length)); attr_len = le32_to_cpu(ctx->attr->data.resident.value_length);
addr = kmap(page); addr = kmap(page);
/* Copy over in bounds data, zeroing the remainder of the page. */ /* Copy over in bounds data, zeroing the remainder of the page. */
...@@ -418,8 +418,8 @@ int ntfs_readpage(struct file *file, struct page *page) ...@@ -418,8 +418,8 @@ int ntfs_readpage(struct file *file, struct page *page)
memset(addr + bytes, 0, PAGE_CACHE_SIZE - bytes); memset(addr + bytes, 0, PAGE_CACHE_SIZE - bytes);
/* Copy the data to the page. */ /* Copy the data to the page. */
memcpy(addr, attr_pos + (char*)ctx->attr + memcpy(addr, attr_pos + (char*)ctx->attr +
le16_to_cpu(ctx->attr->_ARA(value_offset)), le16_to_cpu(
bytes); ctx->attr->data.resident.value_offset), bytes);
} else } else
memset(addr, 0, PAGE_CACHE_SIZE); memset(addr, 0, PAGE_CACHE_SIZE);
flush_dcache_page(page); flush_dcache_page(page);
...@@ -892,7 +892,7 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc) ...@@ -892,7 +892,7 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
if (!NInoAttr(ni)) if (!NInoAttr(ni))
base_ni = ni; base_ni = ni;
else else
base_ni = ni->_INE(base_ntfs_ino); base_ni = ni->ext.base_ntfs_ino;
/* Map, pin, and lock the mft record. */ /* Map, pin, and lock the mft record. */
m = map_mft_record(base_ni); m = map_mft_record(base_ni);
...@@ -917,7 +917,7 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc) ...@@ -917,7 +917,7 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
attr_pos = page->index << PAGE_CACHE_SHIFT; attr_pos = page->index << PAGE_CACHE_SHIFT;
/* The total length of the attribute value. */ /* The total length of the attribute value. */
attr_len = le32_to_cpu(ctx->attr->_ARA(value_length)); attr_len = le32_to_cpu(ctx->attr->data.resident.value_length);
if (unlikely(vi->i_size != attr_len)) { if (unlikely(vi->i_size != attr_len)) {
ntfs_error(vi->i_sb, "BUG()! i_size (0x%Lx) doesn't match " ntfs_error(vi->i_sb, "BUG()! i_size (0x%Lx) doesn't match "
...@@ -956,8 +956,9 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc) ...@@ -956,8 +956,9 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
kaddr = kmap_atomic(page, KM_USER0); kaddr = kmap_atomic(page, KM_USER0);
/* Copy the data from the page to the mft record. */ /* Copy the data from the page to the mft record. */
memcpy((u8*)ctx->attr + le16_to_cpu(ctx->attr->_ARA(value_offset)) + memcpy((u8*)ctx->attr + le16_to_cpu(
attr_pos, kaddr, bytes); ctx->attr->data.resident.value_offset) + attr_pos,
kaddr, bytes);
flush_dcache_mft_record_page(ctx->ntfs_ino); flush_dcache_mft_record_page(ctx->ntfs_ino);
#if 0 #if 0
/* Zero out of bounds area. */ /* Zero out of bounds area. */
...@@ -1656,7 +1657,7 @@ static int ntfs_commit_write(struct file *file, struct page *page, ...@@ -1656,7 +1657,7 @@ static int ntfs_commit_write(struct file *file, struct page *page,
if (!NInoAttr(ni)) if (!NInoAttr(ni))
base_ni = ni; base_ni = ni;
else else
base_ni = ni->_INE(base_ntfs_ino); base_ni = ni->ext.base_ntfs_ino;
/* Map, pin, and lock the mft record. */ /* Map, pin, and lock the mft record. */
m = map_mft_record(base_ni); m = map_mft_record(base_ni);
...@@ -1681,7 +1682,7 @@ static int ntfs_commit_write(struct file *file, struct page *page, ...@@ -1681,7 +1682,7 @@ static int ntfs_commit_write(struct file *file, struct page *page,
attr_pos = page->index << PAGE_CACHE_SHIFT; attr_pos = page->index << PAGE_CACHE_SHIFT;
/* The total length of the attribute value. */ /* The total length of the attribute value. */
attr_len = le32_to_cpu(ctx->attr->_ARA(value_length)); attr_len = le32_to_cpu(ctx->attr->data.resident.value_length);
if (unlikely(vi->i_size != attr_len)) { if (unlikely(vi->i_size != attr_len)) {
ntfs_error(vi->i_sb, "BUG()! i_size (0x%Lx) doesn't match " ntfs_error(vi->i_sb, "BUG()! i_size (0x%Lx) doesn't match "
...@@ -1705,8 +1706,8 @@ static int ntfs_commit_write(struct file *file, struct page *page, ...@@ -1705,8 +1706,8 @@ static int ntfs_commit_write(struct file *file, struct page *page,
* Calculate the address of the attribute value corresponding to the * Calculate the address of the attribute value corresponding to the
* beginning of the current data @page. * beginning of the current data @page.
*/ */
kattr = (u8*)ctx->attr + le16_to_cpu(ctx->attr->_ARA(value_offset)) + kattr = (u8*)ctx->attr + le16_to_cpu(
attr_pos; ctx->attr->data.resident.value_offset) + attr_pos;
kaddr = kmap_atomic(page, KM_USER0); kaddr = kmap_atomic(page, KM_USER0);
......
/** /**
* attrib.c - NTFS attribute operations. Part of the Linux-NTFS project. * attrib.c - NTFS attribute operations. Part of the Linux-NTFS project.
* *
* Copyright (c) 2001,2002 Anton Altaparmakov. * Copyright (c) 2001-2003 Anton Altaparmakov
* Copyright (C) 2002 Richard Russon. * Copyright (c) 2002 Richard Russon
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published * modify it under the terms of the GNU General Public License as published
...@@ -731,17 +731,18 @@ run_list_element *decompress_mapping_pairs(const ntfs_volume *vol, ...@@ -731,17 +731,18 @@ run_list_element *decompress_mapping_pairs(const ntfs_volume *vol,
#ifdef DEBUG #ifdef DEBUG
/* Make sure attr exists and is non-resident. */ /* Make sure attr exists and is non-resident. */
if (!attr || !attr->non_resident || if (!attr || !attr->non_resident || sle64_to_cpu(
sle64_to_cpu(attr->_ANR(lowest_vcn)) < (VCN)0) { attr->data.non_resident.lowest_vcn) < (VCN)0) {
ntfs_error(vol->sb, "Invalid arguments."); ntfs_error(vol->sb, "Invalid arguments.");
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
#endif #endif
/* Start at vcn = lowest_vcn and lcn 0. */ /* Start at vcn = lowest_vcn and lcn 0. */
vcn = sle64_to_cpu(attr->_ANR(lowest_vcn)); vcn = sle64_to_cpu(attr->data.non_resident.lowest_vcn);
lcn = 0; lcn = 0;
/* Get start of the mapping pairs array. */ /* Get start of the mapping pairs array. */
buf = (u8*)attr + le16_to_cpu(attr->_ANR(mapping_pairs_offset)); buf = (u8*)attr + le16_to_cpu(
attr->data.non_resident.mapping_pairs_offset);
attr_end = (u8*)attr + le32_to_cpu(attr->length); attr_end = (u8*)attr + le32_to_cpu(attr->length);
if (unlikely(buf < (u8*)attr || buf > attr_end)) { if (unlikely(buf < (u8*)attr || buf > attr_end)) {
ntfs_error(vol->sb, "Corrupt attribute."); ntfs_error(vol->sb, "Corrupt attribute.");
...@@ -867,7 +868,7 @@ run_list_element *decompress_mapping_pairs(const ntfs_volume *vol, ...@@ -867,7 +868,7 @@ run_list_element *decompress_mapping_pairs(const ntfs_volume *vol,
* If there is a highest_vcn specified, it must be equal to the final * If there is a highest_vcn specified, it must be equal to the final
* vcn in the run list - 1, or something has gone badly wrong. * vcn in the run list - 1, or something has gone badly wrong.
*/ */
deltaxcn = sle64_to_cpu(attr->_ANR(highest_vcn)); deltaxcn = sle64_to_cpu(attr->data.non_resident.highest_vcn);
if (unlikely(deltaxcn && vcn - 1 != deltaxcn)) { if (unlikely(deltaxcn && vcn - 1 != deltaxcn)) {
mpa_err: mpa_err:
ntfs_error(vol->sb, "Corrupt mapping pairs array in " ntfs_error(vol->sb, "Corrupt mapping pairs array in "
...@@ -875,10 +876,11 @@ run_list_element *decompress_mapping_pairs(const ntfs_volume *vol, ...@@ -875,10 +876,11 @@ run_list_element *decompress_mapping_pairs(const ntfs_volume *vol,
goto err_out; goto err_out;
} }
/* Setup not mapped run list element if this is the base extent. */ /* Setup not mapped run list element if this is the base extent. */
if (!attr->_ANR(lowest_vcn)) { if (!attr->data.non_resident.lowest_vcn) {
VCN max_cluster; VCN max_cluster;
max_cluster = (sle64_to_cpu(attr->_ANR(allocated_size)) + max_cluster = (sle64_to_cpu(
attr->data.non_resident.allocated_size) +
vol->cluster_size - 1) >> vol->cluster_size - 1) >>
vol->cluster_size_bits; vol->cluster_size_bits;
/* /*
...@@ -951,7 +953,7 @@ int map_run_list(ntfs_inode *ni, VCN vcn) ...@@ -951,7 +953,7 @@ int map_run_list(ntfs_inode *ni, VCN vcn)
if (!NInoAttr(ni)) if (!NInoAttr(ni))
base_ni = ni; base_ni = ni;
else else
base_ni = ni->_INE(base_ntfs_ino); base_ni = ni->ext.base_ntfs_ino;
mrec = map_mft_record(base_ni); mrec = map_mft_record(base_ni);
if (IS_ERR(mrec)) if (IS_ERR(mrec))
...@@ -1180,19 +1182,23 @@ BOOL find_attr(const ATTR_TYPES type, const uchar_t *name, const u32 name_len, ...@@ -1180,19 +1182,23 @@ BOOL find_attr(const ATTR_TYPES type, const uchar_t *name, const u32 name_len,
return TRUE; return TRUE;
/* @val is present; compare values. */ /* @val is present; compare values. */
else { else {
u32 vl;
register int rc; register int rc;
vl = le32_to_cpu(a->data.resident.value_length);
if (vl > val_len)
vl = val_len;
rc = memcmp(val, (u8*)a + le16_to_cpu( rc = memcmp(val, (u8*)a + le16_to_cpu(
a->_ARA(value_offset)), a->data.resident.value_offset), vl);
min_t(const u32, val_len,
le32_to_cpu(a->_ARA(value_length))));
/* /*
* If @val collates before the current attribute's * If @val collates before the current attribute's
* value, there is no matching attribute. * value, there is no matching attribute.
*/ */
if (!rc) { if (!rc) {
register u32 avl; register u32 avl;
avl = le32_to_cpu(a->_ARA(value_length)); avl = le32_to_cpu(
a->data.resident.value_length);
if (val_len == avl) if (val_len == avl)
return TRUE; return TRUE;
if (val_len < avl) if (val_len < avl)
...@@ -1235,11 +1241,9 @@ int load_attribute_list(ntfs_volume *vol, run_list *run_list, u8 *al, ...@@ -1235,11 +1241,9 @@ int load_attribute_list(ntfs_volume *vol, run_list *run_list, u8 *al,
unsigned char block_size_bits = sb->s_blocksize_bits; unsigned char block_size_bits = sb->s_blocksize_bits;
ntfs_debug("Entering."); ntfs_debug("Entering.");
#ifdef DEBUG
if (!vol || !run_list || !al || size <= 0 || initialized_size < 0 || if (!vol || !run_list || !al || size <= 0 || initialized_size < 0 ||
initialized_size > size) initialized_size > size)
return -EINVAL; return -EINVAL;
#endif
if (!initialized_size) { if (!initialized_size) {
memset(al, 0, size); memset(al, 0, size);
return 0; return 0;
...@@ -1270,8 +1274,8 @@ int load_attribute_list(ntfs_volume *vol, run_list *run_list, u8 *al, ...@@ -1270,8 +1274,8 @@ int load_attribute_list(ntfs_volume *vol, run_list *run_list, u8 *al,
"read attribute list."); "read attribute list.");
goto err_out; goto err_out;
} }
if (al + block_size > al_end) if (al + block_size >= al_end)
goto do_partial; goto do_final;
memcpy(al, bh->b_data, block_size); memcpy(al, bh->b_data, block_size);
brelse(bh); brelse(bh);
al += block_size; al += block_size;
...@@ -1285,7 +1289,7 @@ int load_attribute_list(ntfs_volume *vol, run_list *run_list, u8 *al, ...@@ -1285,7 +1289,7 @@ int load_attribute_list(ntfs_volume *vol, run_list *run_list, u8 *al,
done: done:
up_read(&run_list->lock); up_read(&run_list->lock);
return err; return err;
do_partial: do_final:
if (al < al_end) { if (al < al_end) {
/* Partial block. */ /* Partial block. */
memcpy(al, bh->b_data, al_end - al); memcpy(al, bh->b_data, al_end - al);
...@@ -1546,9 +1550,11 @@ static BOOL find_external_attr(const ATTR_TYPES type, const uchar_t *name, ...@@ -1546,9 +1550,11 @@ static BOOL find_external_attr(const ATTR_TYPES type, const uchar_t *name,
* If no @val specified or @val specified and it matches, we * If no @val specified or @val specified and it matches, we
* have found it! * have found it!
*/ */
if (!val || (!a->non_resident && le32_to_cpu(a->_ARA(value_length)) if (!val || (!a->non_resident && le32_to_cpu(
== val_len && !memcmp((u8*)a + a->data.resident.value_length) == val_len &&
le16_to_cpu(a->_ARA(value_offset)), val, val_len))) { !memcmp((u8*)a +
le16_to_cpu(a->data.resident.value_offset),
val, val_len))) {
ntfs_debug("Done, found."); ntfs_debug("Done, found.");
return TRUE; return TRUE;
} }
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
* attrib.h - Defines for attribute handling in NTFS Linux kernel driver. * attrib.h - Defines for attribute handling in NTFS Linux kernel driver.
* Part of the Linux-NTFS project. * Part of the Linux-NTFS project.
* *
* Copyright (c) 2001,2002 Anton Altaparmakov. * Copyright (c) 2001-2003 Anton Altaparmakov
* Copyright (C) 2002 Richard Russon. * Copyright (c) 2002 Richard Russon
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published * modify it under the terms of the GNU General Public License as published
...@@ -93,8 +93,8 @@ extern int load_attribute_list(ntfs_volume *vol, run_list *rl, u8 *al, ...@@ -93,8 +93,8 @@ extern int load_attribute_list(ntfs_volume *vol, run_list *rl, u8 *al,
static inline s64 attribute_value_length(const ATTR_RECORD *a) static inline s64 attribute_value_length(const ATTR_RECORD *a)
{ {
if (!a->non_resident) if (!a->non_resident)
return (s64)le32_to_cpu(a->_ARA(value_length)); return (s64)le32_to_cpu(a->data.resident.value_length);
return sle64_to_cpu(a->_ANR(data_size)); return sle64_to_cpu(a->data.non_resident.data_size);
} }
extern void reinit_attr_search_ctx(attr_search_context *ctx); extern void reinit_attr_search_ctx(attr_search_context *ctx);
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
* compress.c - NTFS kernel compressed attributes handling. * compress.c - NTFS kernel compressed attributes handling.
* Part of the Linux-NTFS project. * Part of the Linux-NTFS project.
* *
* Copyright (c) 2001,2002 Anton Altaparmakov. * Copyright (c) 2001-2003 Anton Altaparmakov
* Copyright (C) 2002 Richard Russon. * Copyright (c) 2002 Richard Russon
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published * modify it under the terms of the GNU General Public License as published
...@@ -44,7 +44,7 @@ typedef enum { ...@@ -44,7 +44,7 @@ typedef enum {
* The maximum compression block size is by definition 16 * the cluster * The maximum compression block size is by definition 16 * the cluster
* size, with the maximum supported cluster size being 4kiB. Thus the * size, with the maximum supported cluster size being 4kiB. Thus the
* maximum compression buffer size is 64kiB, so we use this when * maximum compression buffer size is 64kiB, so we use this when
* initializing the per-CPU buffers. * initializing the compression buffer.
*/ */
NTFS_MAX_CB_SIZE = 64 * 1024, NTFS_MAX_CB_SIZE = 64 * 1024,
} ntfs_compression_constants; } ntfs_compression_constants;
...@@ -88,6 +88,40 @@ void free_compression_buffers(void) ...@@ -88,6 +88,40 @@ void free_compression_buffers(void)
ntfs_compression_buffer = NULL; ntfs_compression_buffer = NULL;
} }
/**
* zero_partial_compressed_page - zero out of bounds compressed page region
*/
static void zero_partial_compressed_page(ntfs_inode *ni, struct page *page)
{
u8 *kp = page_address(page);
unsigned int kp_ofs;
ntfs_debug("Zeroing page region outside initialized size.");
if (((s64)page->index << PAGE_CACHE_SHIFT) >= ni->initialized_size) {
/*
* FIXME: Using clear_page() will become wrong when we get
* PAGE_CACHE_SIZE != PAGE_SIZE but for now there is no problem.
*/
clear_page(kp);
return;
}
kp_ofs = ni->initialized_size & ~PAGE_CACHE_MASK;
memset(kp + kp_ofs, 0, PAGE_CACHE_SIZE - kp_ofs);
return;
}
/**
* handle_bounds_compressed_page - test for&handle out of bounds compressed page
*/
static inline void handle_bounds_compressed_page(ntfs_inode *ni,
struct page *page)
{
if ((page->index >= (ni->initialized_size >> PAGE_CACHE_SHIFT)) &&
(ni->initialized_size < VFS_I(ni)->i_size))
zero_partial_compressed_page(ni, page);
return;
}
/** /**
* ntfs_decompress - decompress a compression block into an array of pages * ntfs_decompress - decompress a compression block into an array of pages
* @dest_pages: destination array of pages * @dest_pages: destination array of pages
...@@ -164,7 +198,7 @@ static int ntfs_decompress(struct page *dest_pages[], int *dest_index, ...@@ -164,7 +198,7 @@ static int ntfs_decompress(struct page *dest_pages[], int *dest_index,
cb - cb_start); cb - cb_start);
/* Have we reached the end of the compression block? */ /* Have we reached the end of the compression block? */
if (cb == cb_end || !le16_to_cpup(cb)) { if (cb == cb_end || !le16_to_cpup((u16*)cb)) {
int i; int i;
ntfs_debug("Completed. Returning success (0)."); ntfs_debug("Completed. Returning success (0).");
...@@ -173,10 +207,19 @@ static int ntfs_decompress(struct page *dest_pages[], int *dest_index, ...@@ -173,10 +207,19 @@ static int ntfs_decompress(struct page *dest_pages[], int *dest_index,
/* We can sleep from now on, so we drop lock. */ /* We can sleep from now on, so we drop lock. */
spin_unlock(&ntfs_cb_lock); spin_unlock(&ntfs_cb_lock);
/* Second stage: finalize completed pages. */ /* Second stage: finalize completed pages. */
if (nr_completed_pages > 0) {
struct page *page = dest_pages[completed_pages[0]];
ntfs_inode *ni = NTFS_I(page->mapping->host);
for (i = 0; i < nr_completed_pages; i++) { for (i = 0; i < nr_completed_pages; i++) {
int di = completed_pages[i]; int di = completed_pages[i];
dp = dest_pages[di]; dp = dest_pages[di];
/*
* If we are outside the initialized size, zero
* the out of bounds page range.
*/
handle_bounds_compressed_page(ni, dp);
flush_dcache_page(dp); flush_dcache_page(dp);
kunmap(dp); kunmap(dp);
SetPageUptodate(dp); SetPageUptodate(dp);
...@@ -187,6 +230,7 @@ static int ntfs_decompress(struct page *dest_pages[], int *dest_index, ...@@ -187,6 +230,7 @@ static int ntfs_decompress(struct page *dest_pages[], int *dest_index,
page_cache_release(dp); page_cache_release(dp);
dest_pages[di] = NULL; dest_pages[di] = NULL;
} }
}
return err; return err;
} }
...@@ -204,7 +248,8 @@ static int ntfs_decompress(struct page *dest_pages[], int *dest_index, ...@@ -204,7 +248,8 @@ static int ntfs_decompress(struct page *dest_pages[], int *dest_index,
/* Setup the current sub-block source pointers and validate range. */ /* Setup the current sub-block source pointers and validate range. */
cb_sb_start = cb; cb_sb_start = cb;
cb_sb_end = cb_sb_start + (le16_to_cpup(cb) & NTFS_SB_SIZE_MASK) + 3; cb_sb_end = cb_sb_start + (le16_to_cpup((u16*)cb) & NTFS_SB_SIZE_MASK)
+ 3;
if (cb_sb_end > cb_end) if (cb_sb_end > cb_end)
goto return_overflow; goto return_overflow;
...@@ -225,7 +270,7 @@ static int ntfs_decompress(struct page *dest_pages[], int *dest_index, ...@@ -225,7 +270,7 @@ static int ntfs_decompress(struct page *dest_pages[], int *dest_index,
dp_addr = (u8*)page_address(dp) + do_sb_start; dp_addr = (u8*)page_address(dp) + do_sb_start;
/* Now, we are ready to process the current sub-block (sb). */ /* Now, we are ready to process the current sub-block (sb). */
if (!(le16_to_cpup(cb) & NTFS_SB_IS_COMPRESSED)) { if (!(le16_to_cpup((u16*)cb) & NTFS_SB_IS_COMPRESSED)) {
ntfs_debug("Found uncompressed sub-block."); ntfs_debug("Found uncompressed sub-block.");
/* This sb is not compressed, just copy it into destination. */ /* This sb is not compressed, just copy it into destination. */
...@@ -330,7 +375,7 @@ static int ntfs_decompress(struct page *dest_pages[], int *dest_index, ...@@ -330,7 +375,7 @@ static int ntfs_decompress(struct page *dest_pages[], int *dest_index,
lg++; lg++;
/* Get the phrase token into i. */ /* Get the phrase token into i. */
pt = le16_to_cpup(cb); pt = le16_to_cpup((u16*)cb);
/* /*
* Calculate starting position of the byte sequence in * Calculate starting position of the byte sequence in
...@@ -432,7 +477,7 @@ int ntfs_read_compressed_block(struct page *page) ...@@ -432,7 +477,7 @@ int ntfs_read_compressed_block(struct page *page)
u8 *cb, *cb_pos, *cb_end; u8 *cb, *cb_pos, *cb_end;
struct buffer_head **bhs; struct buffer_head **bhs;
unsigned long offset, index = page->index; unsigned long offset, index = page->index;
u32 cb_size = ni->_ICF(compression_block_size); u32 cb_size = ni->itype.compressed.block_size;
u64 cb_size_mask = cb_size - 1UL; u64 cb_size_mask = cb_size - 1UL;
VCN vcn; VCN vcn;
LCN lcn; LCN lcn;
...@@ -447,7 +492,7 @@ int ntfs_read_compressed_block(struct page *page) ...@@ -447,7 +492,7 @@ int ntfs_read_compressed_block(struct page *page)
& ~cb_size_mask) >> vol->cluster_size_bits; & ~cb_size_mask) >> vol->cluster_size_bits;
/* Number of compression blocks (cbs) in the wanted vcn range. */ /* Number of compression blocks (cbs) in the wanted vcn range. */
unsigned int nr_cbs = (end_vcn - start_vcn) << vol->cluster_size_bits unsigned int nr_cbs = (end_vcn - start_vcn) << vol->cluster_size_bits
>> ni->_ICF(compression_block_size_bits); >> ni->itype.compressed.block_size_bits;
/* /*
* Number of pages required to store the uncompressed data from all * Number of pages required to store the uncompressed data from all
* compression blocks (cbs) overlapping @page. Due to alignment * compression blocks (cbs) overlapping @page. Due to alignment
...@@ -528,7 +573,7 @@ int ntfs_read_compressed_block(struct page *page) ...@@ -528,7 +573,7 @@ int ntfs_read_compressed_block(struct page *page)
*/ */
cur_page = 0; cur_page = 0;
cur_ofs = 0; cur_ofs = 0;
cb_clusters = ni->_ICF(compression_block_clusters); cb_clusters = ni->itype.compressed.block_clusters;
do_next_cb: do_next_cb:
nr_cbs--; nr_cbs--;
nr_bhs = 0; nr_bhs = 0;
...@@ -763,6 +808,11 @@ int ntfs_read_compressed_block(struct page *page) ...@@ -763,6 +808,11 @@ int ntfs_read_compressed_block(struct page *page)
for (; cur2_page < cb_max_page; cur2_page++) { for (; cur2_page < cb_max_page; cur2_page++) {
page = pages[cur2_page]; page = pages[cur2_page];
if (page) { if (page) {
/*
* If we are outside the initialized size, zero
* the out of bounds page range.
*/
handle_bounds_compressed_page(ni, page);
flush_dcache_page(page); flush_dcache_page(page);
kunmap(page); kunmap(page);
SetPageUptodate(page); SetPageUptodate(page);
......
This diff is collapsed.
This diff is collapsed.
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
* inode.h - Defines for inode structures NTFS Linux kernel driver. Part of * inode.h - Defines for inode structures NTFS Linux kernel driver. Part of
* the Linux-NTFS project. * the Linux-NTFS project.
* *
* Copyright (c) 2001,2002 Anton Altaparmakov. * Copyright (c) 2001-2003 Anton Altaparmakov
* Copyright (c) 2002 Richard Russon. * Copyright (c) 2002 Richard Russon
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published * modify it under the terms of the GNU General Public License as published
...@@ -93,23 +93,21 @@ struct _ntfs_inode { ...@@ -93,23 +93,21 @@ struct _ntfs_inode {
struct { /* It is a directory or $MFT. */ struct { /* It is a directory or $MFT. */
struct inode *bmp_ino; /* Attribute inode for the struct inode *bmp_ino; /* Attribute inode for the
directory index $BITMAP. */ directory index $BITMAP. */
u32 index_block_size; /* Size of an index block. */ u32 block_size; /* Size of an index block. */
u32 index_vcn_size; /* Size of a vcn in this u32 vcn_size; /* Size of a vcn in this
directory index. */ directory index. */
u8 index_block_size_bits; /* Log2 of the above. */ u8 block_size_bits; /* Log2 of the above. */
u8 index_vcn_size_bits; /* Log2 of the above. */ u8 vcn_size_bits; /* Log2 of the above. */
} SN(idm); } index;
struct { /* It is a compressed file or fake inode. */ struct { /* It is a compressed file or fake inode. */
s64 compressed_size; /* Copy from $DATA. */ s64 size; /* Copy of compressed_size from
u32 compression_block_size; /* Size of a compression $DATA. */
block (cb). */ u32 block_size; /* Size of a compression block
u8 compression_block_size_bits; /* Log2 of the size of (cb). */
a cb. */ u8 block_size_bits; /* Log2 of the size of a cb. */
u8 compression_block_clusters; /* Number of clusters u8 block_clusters; /* Number of clusters per cb. */
per compression } compressed;
block. */ } itype;
} SN(icf);
} SN(idc);
struct semaphore extent_lock; /* Lock for accessing/modifying the struct semaphore extent_lock; /* Lock for accessing/modifying the
below . */ below . */
s32 nr_extents; /* For a base mft record, the number of attached extent s32 nr_extents; /* For a base mft record, the number of attached extent
...@@ -126,13 +124,9 @@ struct _ntfs_inode { ...@@ -126,13 +124,9 @@ struct _ntfs_inode {
record. For fake inodes, the record. For fake inodes, the
real (base) inode to which real (base) inode to which
the attribute belongs. */ the attribute belongs. */
} SN(ine); } ext;
}; };
#define _IDM(X) SC(idc.idm,X)
#define _ICF(X) SC(idc.icf,X)
#define _INE(X) SC(ine,X)
/* /*
* Defined bits for the state field in the ntfs_inode structure. * Defined bits for the state field in the ntfs_inode structure.
* (f) = files only, (d) = directories only, (a) = attributes/fake inodes only * (f) = files only, (d) = directories only, (a) = attributes/fake inodes only
......
This diff is collapsed.
/** /**
* mft.c - NTFS kernel mft record operations. Part of the Linux-NTFS project. * mft.c - NTFS kernel mft record operations. Part of the Linux-NTFS project.
* *
* Copyright (c) 2001,2002 Anton Altaparmakov. * Copyright (c) 2001-2003 Anton Altaparmakov
* Copyright (c) 2002 Richard Russon. * Copyright (c) 2002 Richard Russon
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published * modify it under the terms of the GNU General Public License as published
...@@ -39,18 +39,18 @@ static void __format_mft_record(MFT_RECORD *m, const int size, ...@@ -39,18 +39,18 @@ static void __format_mft_record(MFT_RECORD *m, const int size,
ATTR_RECORD *a; ATTR_RECORD *a;
memset(m, 0, size); memset(m, 0, size);
m->_MNR(magic) = magic_FILE; m->magic = magic_FILE;
/* Aligned to 2-byte boundary. */ /* Aligned to 2-byte boundary. */
m->_MNR(usa_ofs) = cpu_to_le16((sizeof(MFT_RECORD) + 1) & ~1); m->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD) + 1) & ~1);
m->_MNR(usa_count) = cpu_to_le16(size / NTFS_BLOCK_SIZE + 1); m->usa_count = cpu_to_le16(size / NTFS_BLOCK_SIZE + 1);
/* Set the update sequence number to 1. */ /* Set the update sequence number to 1. */
*(u16*)((char*)m + ((sizeof(MFT_RECORD) + 1) & ~1)) = cpu_to_le16(1); *(u16*)((char*)m + ((sizeof(MFT_RECORD) + 1) & ~1)) = cpu_to_le16(1);
m->lsn = cpu_to_le64(0LL); m->lsn = cpu_to_le64(0LL);
m->sequence_number = cpu_to_le16(1); m->sequence_number = cpu_to_le16(1);
m->link_count = cpu_to_le16(0); m->link_count = cpu_to_le16(0);
/* Aligned to 8-byte boundary. */ /* Aligned to 8-byte boundary. */
m->attrs_offset = cpu_to_le16((le16_to_cpu(m->_MNR(usa_ofs)) + m->attrs_offset = cpu_to_le16((le16_to_cpu(m->usa_ofs) +
(le16_to_cpu(m->_MNR(usa_count)) << 1) + 7) & ~7); (le16_to_cpu(m->usa_count) << 1) + 7) & ~7);
m->flags = cpu_to_le16(0); m->flags = cpu_to_le16(0);
/* /*
* Using attrs_offset plus eight bytes (for the termination attribute), * Using attrs_offset plus eight bytes (for the termination attribute),
...@@ -329,7 +329,7 @@ MFT_RECORD *map_extent_mft_record(ntfs_inode *base_ni, MFT_REF mref, ...@@ -329,7 +329,7 @@ MFT_RECORD *map_extent_mft_record(ntfs_inode *base_ni, MFT_REF mref,
*/ */
down(&base_ni->extent_lock); down(&base_ni->extent_lock);
if (base_ni->nr_extents > 0) { if (base_ni->nr_extents > 0) {
extent_nis = base_ni->_INE(extent_ntfs_inos); extent_nis = base_ni->ext.extent_ntfs_inos;
for (i = 0; i < base_ni->nr_extents; i++) { for (i = 0; i < base_ni->nr_extents; i++) {
if (mft_no != extent_nis[i]->mft_no) if (mft_no != extent_nis[i]->mft_no)
continue; continue;
...@@ -374,7 +374,7 @@ MFT_RECORD *map_extent_mft_record(ntfs_inode *base_ni, MFT_REF mref, ...@@ -374,7 +374,7 @@ MFT_RECORD *map_extent_mft_record(ntfs_inode *base_ni, MFT_REF mref,
ni->vol = base_ni->vol; ni->vol = base_ni->vol;
ni->seq_no = seq_no; ni->seq_no = seq_no;
ni->nr_extents = -1; ni->nr_extents = -1;
ni->_INE(base_ntfs_ino) = base_ni; ni->ext.base_ntfs_ino = base_ni;
/* Now map the record. */ /* Now map the record. */
m = map_mft_record(ni); m = map_mft_record(ni);
if (unlikely(IS_ERR(m))) { if (unlikely(IS_ERR(m))) {
...@@ -404,14 +404,14 @@ MFT_RECORD *map_extent_mft_record(ntfs_inode *base_ni, MFT_REF mref, ...@@ -404,14 +404,14 @@ MFT_RECORD *map_extent_mft_record(ntfs_inode *base_ni, MFT_REF mref,
m = ERR_PTR(-ENOMEM); m = ERR_PTR(-ENOMEM);
goto unm_err_out; goto unm_err_out;
} }
if (base_ni->_INE(extent_ntfs_inos)) { if (base_ni->ext.extent_ntfs_inos) {
memcpy(tmp, base_ni->_INE(extent_ntfs_inos), new_size - memcpy(tmp, base_ni->ext.extent_ntfs_inos, new_size -
4 * sizeof(ntfs_inode *)); 4 * sizeof(ntfs_inode *));
kfree(base_ni->_INE(extent_ntfs_inos)); kfree(base_ni->ext.extent_ntfs_inos);
} }
base_ni->_INE(extent_ntfs_inos) = tmp; base_ni->ext.extent_ntfs_inos = tmp;
} }
base_ni->_INE(extent_ntfs_inos)[base_ni->nr_extents++] = ni; base_ni->ext.extent_ntfs_inos[base_ni->nr_extents++] = ni;
up(&base_ni->extent_lock); up(&base_ni->extent_lock);
atomic_dec(&base_ni->count); atomic_dec(&base_ni->count);
ntfs_debug("Done 2."); ntfs_debug("Done 2.");
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* namei.c - NTFS kernel directory inode operations. Part of the Linux-NTFS * namei.c - NTFS kernel directory inode operations. Part of the Linux-NTFS
* project. * project.
* *
* Copyright (c) 2001,2002 Anton Altaparmakov. * Copyright (c) 2001-2003 Anton Altaparmakov
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published * modify it under the terms of the GNU General Public License as published
...@@ -210,12 +210,12 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent) ...@@ -210,12 +210,12 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent)
a = ctx->attr; a = ctx->attr;
if (a->non_resident || a->flags) if (a->non_resident || a->flags)
goto eio_err_out; goto eio_err_out;
val_len = le32_to_cpu(a->_ARA(value_length)); val_len = le32_to_cpu(a->data.resident.value_length);
if (le16_to_cpu(a->_ARA(value_offset)) + val_len > if (le16_to_cpu(a->data.resident.value_offset) +
le32_to_cpu(a->length)) val_len > le32_to_cpu(a->length))
goto eio_err_out; goto eio_err_out;
fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + le16_to_cpu( fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + le16_to_cpu(
ctx->attr->_ARA(value_offset))); ctx->attr->data.resident.value_offset));
if ((u32)(fn->file_name_length * sizeof(uchar_t) + if ((u32)(fn->file_name_length * sizeof(uchar_t) +
sizeof(FILE_NAME_ATTR)) > val_len) sizeof(FILE_NAME_ATTR)) > val_len)
goto eio_err_out; goto eio_err_out;
......
This diff is collapsed.
/* /*
* unistr.c - NTFS Unicode string handling. Part of the Linux-NTFS project. * unistr.c - NTFS Unicode string handling. Part of the Linux-NTFS project.
* *
* Copyright (c) 2001 Anton Altaparmakov. * Copyright (c) 2001-2003 Anton Altaparmakov
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published * modify it under the terms of the GNU General Public License as published
...@@ -96,10 +96,12 @@ int ntfs_collate_names(const uchar_t *name1, const u32 name1_len, ...@@ -96,10 +96,12 @@ int ntfs_collate_names(const uchar_t *name1, const u32 name1_len,
const int err_val, const IGNORE_CASE_BOOL ic, const int err_val, const IGNORE_CASE_BOOL ic,
const uchar_t *upcase, const u32 upcase_len) const uchar_t *upcase, const u32 upcase_len)
{ {
u32 cnt; u32 cnt, min_len;
const u32 min_len = min_t(const u32, name1_len, name2_len);
uchar_t c1, c2; uchar_t c1, c2;
min_len = name1_len;
if (name1_len > name2_len)
min_len = name2_len;
for (cnt = 0; cnt < min_len; ++cnt) { for (cnt = 0; cnt < min_len; ++cnt) {
c1 = le16_to_cpu(*name1++); c1 = le16_to_cpu(*name1++);
c2 = le16_to_cpu(*name2++); c2 = le16_to_cpu(*name2++);
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
* upcase.c - Generate the full NTFS Unicode upcase table in little endian. * upcase.c - Generate the full NTFS Unicode upcase table in little endian.
* Part of the Linux-NTFS project. * Part of the Linux-NTFS project.
* *
* Copyright (C) 2001 Richard Russon <ntfs@flatcap.org> * Copyright (c) 2001 Richard Russon <ntfs@flatcap.org>
* Copyright (c) 2001,2002 Anton Altaparmakov * Copyright (c) 2001-2003 Anton Altaparmakov
* *
* Modified for mkntfs inclusion 9 June 2001 by Anton Altaparmakov. * Modified for mkntfs inclusion 9 June 2001 by Anton Altaparmakov.
* Modified for kernel inclusion 10 September 2001 by Anton Altparmakov. * Modified for kernel inclusion 10 September 2001 by Anton Altparmakov.
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
uchar_t *generate_default_upcase(void) uchar_t *generate_default_upcase(void)
{ {
const int uc_run_table[][3] = { /* Start, End, Add */ static const int uc_run_table[][3] = { /* Start, End, Add */
{0x0061, 0x007B, -32}, {0x0451, 0x045D, -80}, {0x1F70, 0x1F72, 74}, {0x0061, 0x007B, -32}, {0x0451, 0x045D, -80}, {0x1F70, 0x1F72, 74},
{0x00E0, 0x00F7, -32}, {0x045E, 0x0460, -80}, {0x1F72, 0x1F76, 86}, {0x00E0, 0x00F7, -32}, {0x045E, 0x0460, -80}, {0x1F72, 0x1F76, 86},
{0x00F8, 0x00FF, -32}, {0x0561, 0x0587, -48}, {0x1F76, 0x1F78, 100}, {0x00F8, 0x00FF, -32}, {0x0561, 0x0587, -48}, {0x1F76, 0x1F78, 100},
...@@ -45,7 +45,7 @@ uchar_t *generate_default_upcase(void) ...@@ -45,7 +45,7 @@ uchar_t *generate_default_upcase(void)
{0} {0}
}; };
const int uc_dup_table[][2] = { /* Start, End */ static const int uc_dup_table[][2] = { /* Start, End */
{0x0100, 0x012F}, {0x01A0, 0x01A6}, {0x03E2, 0x03EF}, {0x04CB, 0x04CC}, {0x0100, 0x012F}, {0x01A0, 0x01A6}, {0x03E2, 0x03EF}, {0x04CB, 0x04CC},
{0x0132, 0x0137}, {0x01B3, 0x01B7}, {0x0460, 0x0481}, {0x04D0, 0x04EB}, {0x0132, 0x0137}, {0x01B3, 0x01B7}, {0x0460, 0x0481}, {0x04D0, 0x04EB},
{0x0139, 0x0149}, {0x01CD, 0x01DD}, {0x0490, 0x04BF}, {0x04EE, 0x04F5}, {0x0139, 0x0149}, {0x01CD, 0x01DD}, {0x0490, 0x04BF}, {0x04EE, 0x04F5},
...@@ -55,7 +55,7 @@ uchar_t *generate_default_upcase(void) ...@@ -55,7 +55,7 @@ uchar_t *generate_default_upcase(void)
{0} {0}
}; };
const int uc_word_table[][2] = { /* Offset, Value */ static const int uc_word_table[][2] = { /* Offset, Value */
{0x00FF, 0x0178}, {0x01AD, 0x01AC}, {0x01F3, 0x01F1}, {0x0269, 0x0196}, {0x00FF, 0x0178}, {0x01AD, 0x01AC}, {0x01F3, 0x01F1}, {0x0269, 0x0196},
{0x0183, 0x0182}, {0x01B0, 0x01AF}, {0x0253, 0x0181}, {0x026F, 0x019C}, {0x0183, 0x0182}, {0x01B0, 0x01AF}, {0x0253, 0x0181}, {0x026F, 0x019C},
{0x0185, 0x0184}, {0x01B9, 0x01B8}, {0x0254, 0x0186}, {0x0272, 0x019D}, {0x0185, 0x0184}, {0x01B9, 0x01B8}, {0x0254, 0x0186}, {0x0272, 0x019D},
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment