Skip to contentSkip to conversation

fs/adfs updates for 5.6

PATCH New Russell King - ARM Linux admin 4 participants 47 messages Dec 9, 2019

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover fs/adfs updates for 5.6

Hi,

This patch series updates the fs/adfs code in the kernel (which still has users!)

- update inode timestamps to centisecond resolution time, as per the native format.
- consolidate and clean up map scanning code.
- consolidate and clean up and modernise directory handling code.
- restructure directory handling code to better improve the rudimentary write support we have.
- fix inode dropping; otherwise updates are lost on umount.
- add support for E and E+ format image files.

Patches based on v5.4; there have been no changes to fs/adfs during the merge window.

This is also available from:

git://git.armlinux.org.uk/~rmk/linux-arm.git adfs

with head SHA1 8901af013ecd.

 Documentation/filesystems/adfs.txt |  24 +++
 fs/adfs/adfs.h                     |  32 ++--
 fs/adfs/dir.c                      | 314 +++++++++++++++++++++++++--------
 fs/adfs/dir_f.c                    | 302 +++++++++-----------------------
 fs/adfs/dir_f.h                    |  52 +++---
 fs/adfs/dir_fplus.c                | 345 +++++++++++++++++++++----------------
 fs/adfs/dir_fplus.h                |   6 +-
 fs/adfs/inode.c                    |  64 +++----
 fs/adfs/map.c                      | 247 +++++++++++++++++++-------
 fs/adfs/super.c                    | 267 ++++++++++------------------
 10 files changed, 913 insertions(+), 740 deletions(-)
Signature · 3 lines
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 12.1Mbps down 622kbps up
According to speedtest.net: 11.9Mbps down 500kbps up
#1 lore raw

PATCH 1/41 fs/adfs: inode: update timestamps to centisecond precision

Despite ADFS timestamps having centi-second granularity, and Linux gaining fine-grained timestamp support in v2.5.48, fs/adfs was never updated.

Update fs/adfs to centi-second support, and ensure that the inode ctime always reflects what is written in underlying media.

---
 fs/adfs/inode.c | 40 ++++++++++++++++++++--------------------
 fs/adfs/super.c |  2 ++
 2 files changed, 22 insertions(+), 20 deletions(-)
fs/adfs/inode.c +20 -20
index 124de75413a5..18a1d478669b 100644
@@ -158,6 +158,8 @@ adfs_mode2atts(struct super_block *sb, struct inode *inode)
 	return attr;
 }
 
+static const s64 nsec_unix_epoch_diff_risc_os_epoch = 2208988800000000000LL;
+
 /*
  * Convert an ADFS time to Unix time.  ADFS has a 40-bit centi-second time
  * referenced to 1 Jan 1900 (til 2248) so we need to discard 2208988800 seconds
@@ -170,8 +172,6 @@ adfs_adfs2unix_time(struct timespec64 *tv, struct inode *inode)
 	/* 01 Jan 1970 00:00:00 (Unix epoch) as nanoseconds since
 	 * 01 Jan 1900 00:00:00 (RISC OS epoch)
 	 */
-	static const s64 nsec_unix_epoch_diff_risc_os_epoch =
-							2208988800000000000LL;
 	s64 nsec;
 
 	if (!adfs_inode_is_stamped(inode))
@@ -204,24 +204,23 @@ adfs_adfs2unix_time(struct timespec64 *tv, struct inode *inode)
 	return;
 }
 
-/*
- * Convert an Unix time to ADFS time.  We only do this if the entry has a
- * time/date stamp already.
- */
-static void
-adfs_unix2adfs_time(struct inode *inode, unsigned int secs)
+/* Convert an Unix time to ADFS time for an entry that is already stamped. */
+static void adfs_unix2adfs_time(struct inode *inode,
+				const struct timespec64 *ts)
 {
-	unsigned int high, low;
+	s64 cs, nsec = timespec64_to_ns(ts);
 
-	if (adfs_inode_is_stamped(inode)) {
-		/* convert 32-bit seconds to 40-bit centi-seconds */
-		low  = (secs & 255) * 100;
-		high = (secs / 256) * 100 + (low >> 8) + 0x336e996a;
+	/* convert from Unix to RISC OS epoch */
+	nsec += nsec_unix_epoch_diff_risc_os_epoch;
 
-		ADFS_I(inode)->loadaddr = (high >> 24) |
-				(ADFS_I(inode)->loadaddr & ~0xff);
-		ADFS_I(inode)->execaddr = (low & 255) | (high << 8);
-	}
+	/* convert from nanoseconds to centiseconds */
+	cs = div_s64(nsec, 10000000);
+
+	cs = clamp_t(s64, cs, 0, 0xffffffffff);
+
+	ADFS_I(inode)->loadaddr &= ~0xff;
+	ADFS_I(inode)->loadaddr |= (cs >> 32) & 0xff;
+	ADFS_I(inode)->execaddr = cs;
 }
 
 /*
@@ -315,10 +314,11 @@ adfs_notify_change(struct dentry *dentry, struct iattr *attr)
 	if (ia_valid & ATTR_SIZE)
 		truncate_setsize(inode, attr->ia_size);
 
-	if (ia_valid & ATTR_MTIME) {
-		inode->i_mtime = attr->ia_mtime;
-		adfs_unix2adfs_time(inode, attr->ia_mtime.tv_sec);
+	if (ia_valid & ATTR_MTIME && adfs_inode_is_stamped(inode)) {
+		adfs_unix2adfs_time(inode, &attr->ia_mtime);
+		adfs_adfs2unix_time(&inode->i_mtime, inode);
 	}
+
 	/*
 	 * FIXME: should we make these == to i_mtime since we don't
 	 * have the ability to represent them in our filesystem?
fs/adfs/super.c +2 -0
index 65b04ebb51c3..e0eea9adb4e6 100644
@@ -391,7 +391,9 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent)
 	asb = kzalloc(sizeof(*asb), GFP_KERNEL);
 	if (!asb)
 		return -ENOMEM;
+
 	sb->s_fs_info = asb;
+	sb->s_time_gran = 10000000;
 
 	/* set default options */
 	asb->s_uid = GLOBAL_ROOT_UID;
Signature · 1 line
2.20.1
#2 lore raw

Re: [PATCH 01/41] fs/adfs: inode: update timestamps to centisecond precision

Quoted 105 lines · Russell King

Despite ADFS timestamps having centi-second granularity, and Linux gaining fine-grained timestamp support in v2.5.48, fs/adfs was never updated.

Update fs/adfs to centi-second support, and ensure that the inode ctime
always reflects what is written in underlying media.

---
 fs/adfs/inode.c | 40 ++++++++++++++++++++--------------------
 fs/adfs/super.c |  2 ++
 2 files changed, 22 insertions(+), 20 deletions(-)
fs/adfs/inode.c +2 -0
index 124de75413a5..18a1d478669b 100644
@@ -158,6 +158,8 @@ adfs_mode2atts(struct super_block *sb, struct
 inode *inode)
 	return attr;
 }
 
+static const s64 nsec_unix_epoch_diff_risc_os_epoch =
 2208988800000000000LL;
+
 /*

* Convert an ADFS time to Unix time. ADFS has a 40-bit centi-
second time
* referenced to 1 Jan 1900 (til 2248) so we need to discard
2208988800 seconds

fs/adfs/inode.c +0 -2
@@ -170,8 +172,6 @@ adfs_adfs2unix_time(struct timespec64 *tv, struct
 inode *inode)
 	/* 01 Jan 1970 00:00:00 (Unix epoch) as nanoseconds since
 	 * 01 Jan 1900 00:00:00 (RISC OS epoch)
 	 */
-	static const s64 nsec_unix_epoch_diff_risc_os_epoch =
-							220898880000000
 0000LL;
 	s64 nsec;
fs/adfs/inode.c +0 -2
 	if (!adfs_inode_is_stamped(inode))
@@ -204,24 +204,23 @@ adfs_adfs2unix_time(struct timespec64 *tv,
 struct inode *inode)
 	return;
 }
 
-/*
- * Convert an Unix time to ADFS time.  We only do this if the entry

has a
- * time/date stamp already.
- */

fs/adfs/inode.c +1 -2
-static void
-adfs_unix2adfs_time(struct inode *inode, unsigned int secs)
+/* Convert an Unix time to ADFS time for an entry that is already

stamped. */

fs/adfs/inode.c +17 -12
+static void adfs_unix2adfs_time(struct inode *inode,
+				const struct timespec64 *ts)
 {
-	unsigned int high, low;
+	s64 cs, nsec = timespec64_to_ns(ts);
 
-	if (adfs_inode_is_stamped(inode)) {
-		/* convert 32-bit seconds to 40-bit centi-seconds */
-		low  = (secs & 255) * 100;
-		high = (secs / 256) * 100 + (low >> 8) + 0x336e996a;
+	/* convert from Unix to RISC OS epoch */
+	nsec += nsec_unix_epoch_diff_risc_os_epoch;
 
-		ADFS_I(inode)->loadaddr = (high >> 24) |
-				(ADFS_I(inode)->loadaddr & ~0xff);
-		ADFS_I(inode)->execaddr = (low & 255) | (high << 8);
-	}
+	/* convert from nanoseconds to centiseconds */
+	cs = div_s64(nsec, 10000000);
+
+	cs = clamp_t(s64, cs, 0, 0xffffffffff);
+
+	ADFS_I(inode)->loadaddr &= ~0xff;
+	ADFS_I(inode)->loadaddr |= (cs >> 32) & 0xff;
+	ADFS_I(inode)->execaddr = cs;
 }
 
 /*
@@ -315,10 +314,11 @@ adfs_notify_change(struct dentry *dentry,
 struct iattr *attr)
 	if (ia_valid & ATTR_SIZE)
 		truncate_setsize(inode, attr->ia_size);
 
-	if (ia_valid & ATTR_MTIME) {
-		inode->i_mtime = attr->ia_mtime;
-		adfs_unix2adfs_time(inode, attr->ia_mtime.tv_sec);
+	if (ia_valid & ATTR_MTIME && adfs_inode_is_stamped(inode)) {
+		adfs_unix2adfs_time(inode, &attr->ia_mtime);
+		adfs_adfs2unix_time(&inode->i_mtime, inode);
 	}
+
 	/*
 	 * FIXME: should we make these == to i_mtime since we don't
fs/adfs/inode.c +0 -0
 	 * have the ability to represent them in our filesystem?
fs/adfs/super.c +0 -0
index 65b04ebb51c3..e0eea9adb4e6 100644

fs/adfs/super.c static int adfs_fill_super(struct super_block · line 397

Quoted 7 lines
fs/adfs/super.c +2 -0
@@ -391,7 +391,9 @@ static int adfs_fill_super(struct super_block
 *sb, void *data, int silent)
 	asb = kzalloc(sizeof(*asb), GFP_KERNEL);
 	if (!asb)
 		return -ENOMEM;
+
 	sb->s_fs_info = asb;
+	sb->s_time_gran = 10000000;

I believe it's not easy to follow what this granularity means. Maybe, it makes sense to introduce some constant and to add some comment?

Thanks,
Viacheslav Dubeyko.

Quoted 3 lines
fs/adfs/super.c +0 -0
 	/* set default options */
 	asb->s_uid = GLOBAL_ROOT_UID;
#3 lore raw

Re: [PATCH 01/41] fs/adfs: inode: update timestamps to centisecond precision

Quoted 108 lines · Vyacheslav Dubeyko
Quoted 107 lines · Russell King

Despite ADFS timestamps having centi-second granularity, and Linux gaining fine-grained timestamp support in v2.5.48, fs/adfs was never updated.

Update fs/adfs to centi-second support, and ensure that the inode ctime
always reflects what is written in underlying media.

---
 fs/adfs/inode.c | 40 ++++++++++++++++++++--------------------
 fs/adfs/super.c |  2 ++
 2 files changed, 22 insertions(+), 20 deletions(-)
fs/adfs/inode.c +2 -0
index 124de75413a5..18a1d478669b 100644
@@ -158,6 +158,8 @@ adfs_mode2atts(struct super_block *sb, struct
 inode *inode)
 	return attr;
 }
 
+static const s64 nsec_unix_epoch_diff_risc_os_epoch =
 2208988800000000000LL;
+
 /*

* Convert an ADFS time to Unix time. ADFS has a 40-bit centi-
second time
* referenced to 1 Jan 1900 (til 2248) so we need to discard
2208988800 seconds

fs/adfs/inode.c +0 -2
@@ -170,8 +172,6 @@ adfs_adfs2unix_time(struct timespec64 *tv, struct
 inode *inode)
 	/* 01 Jan 1970 00:00:00 (Unix epoch) as nanoseconds since
 	 * 01 Jan 1900 00:00:00 (RISC OS epoch)
 	 */
-	static const s64 nsec_unix_epoch_diff_risc_os_epoch =
-							220898880000000
 0000LL;
 	s64 nsec;
fs/adfs/inode.c +0 -2
 	if (!adfs_inode_is_stamped(inode))
@@ -204,24 +204,23 @@ adfs_adfs2unix_time(struct timespec64 *tv,
 struct inode *inode)
 	return;
 }
 
-/*
- * Convert an Unix time to ADFS time.  We only do this if the entry

has a
- * time/date stamp already.
- */

fs/adfs/inode.c +1 -2
-static void
-adfs_unix2adfs_time(struct inode *inode, unsigned int secs)
+/* Convert an Unix time to ADFS time for an entry that is already

stamped. */

fs/adfs/inode.c +17 -12
+static void adfs_unix2adfs_time(struct inode *inode,
+				const struct timespec64 *ts)
 {
-	unsigned int high, low;
+	s64 cs, nsec = timespec64_to_ns(ts);
 
-	if (adfs_inode_is_stamped(inode)) {
-		/* convert 32-bit seconds to 40-bit centi-seconds */
-		low  = (secs & 255) * 100;
-		high = (secs / 256) * 100 + (low >> 8) + 0x336e996a;
+	/* convert from Unix to RISC OS epoch */
+	nsec += nsec_unix_epoch_diff_risc_os_epoch;
 
-		ADFS_I(inode)->loadaddr = (high >> 24) |
-				(ADFS_I(inode)->loadaddr & ~0xff);
-		ADFS_I(inode)->execaddr = (low & 255) | (high << 8);
-	}
+	/* convert from nanoseconds to centiseconds */
+	cs = div_s64(nsec, 10000000);
+
+	cs = clamp_t(s64, cs, 0, 0xffffffffff);
+
+	ADFS_I(inode)->loadaddr &= ~0xff;
+	ADFS_I(inode)->loadaddr |= (cs >> 32) & 0xff;
+	ADFS_I(inode)->execaddr = cs;
 }
 
 /*
@@ -315,10 +314,11 @@ adfs_notify_change(struct dentry *dentry,
 struct iattr *attr)
 	if (ia_valid & ATTR_SIZE)
 		truncate_setsize(inode, attr->ia_size);
 
-	if (ia_valid & ATTR_MTIME) {
-		inode->i_mtime = attr->ia_mtime;
-		adfs_unix2adfs_time(inode, attr->ia_mtime.tv_sec);
+	if (ia_valid & ATTR_MTIME && adfs_inode_is_stamped(inode)) {
+		adfs_unix2adfs_time(inode, &attr->ia_mtime);
+		adfs_adfs2unix_time(&inode->i_mtime, inode);
 	}
+
 	/*
 	 * FIXME: should we make these == to i_mtime since we don't
fs/adfs/inode.c +0 -0
 	 * have the ability to represent them in our filesystem?
fs/adfs/super.c +0 -0
index 65b04ebb51c3..e0eea9adb4e6 100644
@@ -391,7 +391,9 @@ static int adfs_fill_super(struct super_block
 *sb, void *data, int silent)
 	asb = kzalloc(sizeof(*asb), GFP_KERNEL);
Quoted 8 lines

fs/adfs/super.c static int adfs_fill_super(struct super_block *sb, void *data, int silent) · line 396

Quoted 5 lines
fs/adfs/super.c +2 -0
 	if (!asb)
 		return -ENOMEM;
+
 	sb->s_fs_info = asb;
+	sb->s_time_gran = 10000000;

I believe it's not easy to follow what this granularity means. Maybe, it makes sense to introduce some constant and to add some comment?

Or simply name it "s_time_gran_ns" so the units are in the name.

Signature · 3 lines
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 12.1Mbps down 622kbps up
According to speedtest.net: 11.9Mbps down 500kbps up
#4 lore raw

Re: [PATCH 01/41] fs/adfs: inode: update timestamps to centisecond precision

Quoted 3 lines · Russell King - ARM Linux admin
Quoted 1 line
Quoted 1 line
Quoted 1 line · Vyacheslav Dubeyko

On Mon, 2019-12-09 at 11:08 +0000, Russell King wrote:

<snipped>

Quoted 9 lines
Quoted 6 lines

fs/adfs/super.c static int adfs_fill_super(struct super_block *sb, void *data, int silent) · line 396

Quoted 2 lines
fs/adfs/super.c +1 -0
 	sb->s_fs_info = asb;
+	sb->s_time_gran = 10000000;

I believe it's not easy to follow what this granularity means. Maybe,
it makes sense to introduce some constant and to add some comment?

Or simply name it "s_time_gran_ns" so the units are in the name.

Sounds good. :) But why namely 10000000?

Thanks,
Viacheslav Dubeyko.

#5 lore raw

Re: [PATCH 01/41] fs/adfs: inode: update timestamps to centisecond precision

Quoted 15 lines · Vyacheslav Dubeyko
Quoted 3 lines · Russell King - ARM Linux admin
Quoted 1 line
Quoted 1 line
Quoted 1 line · Vyacheslav Dubeyko

On Mon, 2019-12-09 at 11:08 +0000, Russell King wrote:

<snipped>

Quoted 7 lines
Quoted 6 lines

fs/adfs/super.c static int adfs_fill_super(struct super_block *sb, void *data, int silent) · line 396

Quoted 2 lines
fs/adfs/super.c +1 -0
 	sb->s_fs_info = asb;
+	sb->s_time_gran = 10000000;

I believe it's not easy to follow what this granularity means. Maybe,
it makes sense to introduce some constant and to add some comment?

Quoted 4 lines
Quoted 2 lines

Or simply name it "s_time_gran_ns" so the units are in the name.

Sounds good. :) But why namely 10000000?

I don't know what you mean.

If you're asking, why "10000000", isn't it obvious if you read the commit message? adfs has "centi-second" granularity. s_time_gran is in nanoseconds. There are 10000000 nanoseconds in a centisecond.

What do you expect?

#define ADFS_TIME_GRAN 10000000

	sb->s_time_gran = ADFS_TIME_GRAN;

?

How does that help - it just stupidly and needlessly obfuscates the code.

The whole "use definitions for constants" is idiotic when a constant is only used in one place - when it means you have to search through more source code to find it's single definition. Sorry, I'm not doing that and make readability *worse*.

Signature · 3 lines
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 12.1Mbps down 622kbps up
According to speedtest.net: 11.9Mbps down 500kbps up
#6 lore raw

Re: [PATCH 01/41] fs/adfs: inode: update timestamps to centisecond precision

Quoted 16 lines · Russell King - ARM Linux admin
Quoted 1 line

Sounds good. :) But why namely 10000000?

I don't know what you mean.

If you're asking, why "10000000", isn't it obvious if you read the commit message? adfs has "centi-second" granularity. s_time_gran is in nanoseconds. There are 10000000 nanoseconds in a centisecond.

What do you expect?

#define ADFS_TIME_GRAN 10000000

	sb->s_time_gran = ADFS_TIME_GRAN;

?

Quoted 7 lines

How does that help - it just stupidly and needlessly obfuscates the code.

The whole "use definitions for constants" is idiotic when a constant is only used in one place - when it means you have to search through more source code to find it's single definition. Sorry, I'm not doing that and make readability *worse*.

I'd find it more readable if you wrote it as 10 * 1000 * 1000. Saves trying to count zeroes. I know C added the ability to spell that as 10'000'000, but I don't think all compiler versions support that yet.

Maybe this would be cleanest:

	sb->s_time_gran = NSEC_PER_SEC / 100;

This is definitely how not to do it:

include/acpi/actypes.h:#define ACPI_100NSEC_PER_SEC 10000000L

#7 lore raw

PATCH 2/41 fs/adfs: inode: fix adfs_mode2atts()

In reply to Russell King - ARM Linux admin

Fix adfs_mode2atts() to actually update the file permissions on the media rather than using the current inode mode. Note also that directories do not have read/write permissions stored on the media.

---
 fs/adfs/inode.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)
fs/adfs/inode.c +9 -9
index 18a1d478669b..212a56fc7911 100644
@@ -126,29 +126,29 @@ adfs_atts2mode(struct super_block *sb, struct inode *inode)
  * Convert Linux permission to ADFS attribute.  We try to do the reverse
  * of atts2mode, but there is not a 1:1 translation.
  */
-static int
-adfs_mode2atts(struct super_block *sb, struct inode *inode)
+static int adfs_mode2atts(struct super_block *sb, struct inode *inode,
+			  umode_t ia_mode)
 {
+	struct adfs_sb_info *asb = ADFS_SB(sb);
 	umode_t mode;
 	int attr;
-	struct adfs_sb_info *asb = ADFS_SB(sb);
 
 	/* FIXME: should we be able to alter a link? */
 	if (S_ISLNK(inode->i_mode))
 		return ADFS_I(inode)->attr;
 
+	/* Directories do not have read/write permissions on the media */
 	if (S_ISDIR(inode->i_mode))
-		attr = ADFS_NDA_DIRECTORY;
-	else
-		attr = 0;
+		return ADFS_NDA_DIRECTORY;
 
-	mode = inode->i_mode & asb->s_owner_mask;
+	attr = 0;
+	mode = ia_mode & asb->s_owner_mask;
 	if (mode & S_IRUGO)
 		attr |= ADFS_NDA_OWNER_READ;
 	if (mode & S_IWUGO)
 		attr |= ADFS_NDA_OWNER_WRITE;
 
-	mode = inode->i_mode & asb->s_other_mask;
+	mode = ia_mode & asb->s_other_mask;
 	mode &= ~asb->s_owner_mask;
 	if (mode & S_IRUGO)
 		attr |= ADFS_NDA_PUBLIC_READ;
@@ -328,7 +328,7 @@ adfs_notify_change(struct dentry *dentry, struct iattr *attr)
 	if (ia_valid & ATTR_CTIME)
 		inode->i_ctime = attr->ia_ctime;
 	if (ia_valid & ATTR_MODE) {
-		ADFS_I(inode)->attr = adfs_mode2atts(sb, inode);
+		ADFS_I(inode)->attr = adfs_mode2atts(sb, inode, attr->ia_mode);
 		inode->i_mode = adfs_atts2mode(sb, inode);
 	}
 
Signature · 1 line
2.20.1
#8 lore raw

PATCH 3/41 fs/adfs: map: move map reading and validation to map.c

In reply to Russell King - ARM Linux admin

Keep all the map code together in map.c, rather than having some in super.c

---
 fs/adfs/adfs.h  |  1 +
 fs/adfs/map.c   | 99 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/adfs/super.c | 98 ------------------------------------------------
 3 files changed, 100 insertions(+), 98 deletions(-)
fs/adfs/adfs.h +1 -0
index b7e844d2f321..d23c84aeb6dd 100644
@@ -146,6 +146,7 @@ int adfs_notify_change(struct dentry *dentry, struct iattr *attr);
 /* map.c */
 int adfs_map_lookup(struct super_block *sb, u32 frag_id, unsigned int offset);
 extern unsigned int adfs_map_free(struct super_block *sb);
+struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecord *dr);
 
 /* Misc */
 __printf(3, 4)
fs/adfs/map.c +99 -0

108 lines not on this page · raw message

fs/adfs/super.c +0 -98

110 lines not on this page · raw message

Signature · 1 line
2.20.1
#9 lore raw

PATCH 4/41 fs/adfs: map: rename adfs_map_free() to adfs_map_statfs()

In reply to Russell King - ARM Linux admin

adfs_map_free() is not obvious whether it is freeing the map or returning the number of free blocks on the filesystem. Rename it to the more generic statfs() to make it clear that it's a statistic function.

---
 fs/adfs/adfs.h  |  2 +-
 fs/adfs/map.c   | 10 +++++++---
 fs/adfs/super.c |  7 ++-----
 3 files changed, 10 insertions(+), 9 deletions(-)
fs/adfs/adfs.h +1 -1
index d23c84aeb6dd..45fd48fbd5e0 100644
@@ -145,7 +145,7 @@ int adfs_notify_change(struct dentry *dentry, struct iattr *attr);
 
 /* map.c */
 int adfs_map_lookup(struct super_block *sb, u32 frag_id, unsigned int offset);
-extern unsigned int adfs_map_free(struct super_block *sb);
+void adfs_map_statfs(struct super_block *sb, struct kstatfs *buf);
 struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecord *dr);
 
 /* Misc */
fs/adfs/map.c +7 -3
index 120e01451e75..c322d37e8f91 100644
@@ -5,6 +5,7 @@
  *  Copyright (C) 1997-2002 Russell King
  */
 #include <linux/slab.h>
+#include <linux/statfs.h>
 #include <asm/unaligned.h>
 #include "adfs.h"
 
@@ -221,10 +222,10 @@ static int scan_map(struct adfs_sb_info *asb, unsigned int zone,
  *  total_free = E(free_in_zone_n)
  *              nzones
  */
-unsigned int
-adfs_map_free(struct super_block *sb)
+void adfs_map_statfs(struct super_block *sb, struct kstatfs *buf)
 {
 	struct adfs_sb_info *asb = ADFS_SB(sb);
+	struct adfs_discrecord *dr = adfs_map_discrecord(asb->s_map);
 	struct adfs_discmap *dm;
 	unsigned int total = 0;
 	unsigned int zone;
@@ -236,7 +237,10 @@ adfs_map_free(struct super_block *sb)
 		total += scan_free_map(asb, dm++);
 	} while (--zone > 0);
 
-	return signed_asl(total, asb->s_map2blk);
+	buf->f_blocks  = adfs_disc_size(dr) >> sb->s_blocksize_bits;
+	buf->f_files   = asb->s_ids_per_zone * asb->s_map_size;
+	buf->f_bavail  =
+	buf->f_bfree   = signed_asl(total, asb->s_map2blk);
 }
 
 int adfs_map_lookup(struct super_block *sb, u32 frag_id, unsigned int offset)
fs/adfs/super.c +2 -5
index 4091adb2c7ff..458824e0ca83 100644
@@ -204,16 +204,13 @@ static int adfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
 	struct super_block *sb = dentry->d_sb;
 	struct adfs_sb_info *sbi = ADFS_SB(sb);
-	struct adfs_discrecord *dr = adfs_map_discrecord(sbi->s_map);
 	u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
 
+	adfs_map_statfs(sb, buf);
+
 	buf->f_type    = ADFS_SUPER_MAGIC;
 	buf->f_namelen = sbi->s_namelen;
 	buf->f_bsize   = sb->s_blocksize;
-	buf->f_blocks  = adfs_disc_size(dr) >> sb->s_blocksize_bits;
-	buf->f_files   = sbi->s_ids_per_zone * sbi->s_map_size;
-	buf->f_bavail  =
-	buf->f_bfree   = adfs_map_free(sb);
 	buf->f_ffree   = (long)(buf->f_bfree * buf->f_files) / (long)buf->f_blocks;
 	buf->f_fsid.val[0] = (u32)id;
 	buf->f_fsid.val[1] = (u32)(id >> 32);
Signature · 1 line
2.20.1
#10 lore raw

PATCH 5/41 fs/adfs: map: break up adfs_read_map()

In reply to Russell King - ARM Linux admin

Split up adfs_read_map() into separate helpers to layout the map, read the map, and release the map buffers.

---
 fs/adfs/map.c | 80 +++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 59 insertions(+), 21 deletions(-)
fs/adfs/map.c +59 -21
index c322d37e8f91..4b677cd5d015 100644
@@ -331,12 +331,63 @@ static int adfs_checkmap(struct super_block *sb, struct adfs_discmap *dm)
 	return crosscheck == 0xff && zonecheck;
 }
 
+/*
+ * Layout the map - the first zone contains a copy of the disc record,
+ * and the last zone must be limited to the size of the filesystem.
+ */
+static void adfs_map_layout(struct adfs_discmap *dm, unsigned int nzones,
+			    struct adfs_discrecord *dr)
+{
+	unsigned int zone, zone_size;
+	u64 size;
+
+	zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare);
+
+	dm[0].dm_bh       = NULL;
+	dm[0].dm_startblk = 0;
+	dm[0].dm_startbit = ADFS_DR_SIZE_BITS;
+	dm[0].dm_endbit   = zone_size;
+
+	for (zone = 1; zone < nzones; zone++) {
+		dm[zone].dm_bh       = NULL;
+		dm[zone].dm_startblk = zone * zone_size - ADFS_DR_SIZE_BITS;
+		dm[zone].dm_startbit = 0;
+		dm[zone].dm_endbit   = zone_size;
+	}
+
+	size = adfs_disc_size(dr) >> dr->log2bpmb;
+	size -= (nzones - 1) * zone_size - ADFS_DR_SIZE_BITS;
+	dm[nzones - 1].dm_endbit = size;
+}
+
+static int adfs_map_read(struct adfs_discmap *dm, struct super_block *sb,
+			 unsigned int map_addr, unsigned int nzones)
+{
+	unsigned int zone;
+
+	for (zone = 0; zone < nzones; zone++) {
+		dm[zone].dm_bh = sb_bread(sb, map_addr + zone);
+		if (!dm[zone].dm_bh)
+			return -EIO;
+	}
+
+	return 0;
+}
+
+static void adfs_map_relse(struct adfs_discmap *dm, unsigned int nzones)
+{
+	unsigned int zone;
+
+	for (zone = 0; zone < nzones; zone++)
+		brelse(dm[zone].dm_bh);
+}
+
 struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecord *dr)
 {
+	struct adfs_sb_info *asb = ADFS_SB(sb);
 	struct adfs_discmap *dm;
 	unsigned int map_addr, zone_size, nzones;
-	int i, zone;
-	struct adfs_sb_info *asb = ADFS_SB(sb);
+	int ret;
 
 	nzones    = asb->s_map_size;
 	zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare);
@@ -352,34 +403,21 @@ struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecor
 		return ERR_PTR(-ENOMEM);
 	}
 
-	for (zone = 0; zone < nzones; zone++, map_addr++) {
-		dm[zone].dm_startbit = 0;
-		dm[zone].dm_endbit   = zone_size;
-		dm[zone].dm_startblk = zone * zone_size - ADFS_DR_SIZE_BITS;
-		dm[zone].dm_bh       = sb_bread(sb, map_addr);
+	adfs_map_layout(dm, nzones, dr);
 
-		if (!dm[zone].dm_bh) {
-			adfs_error(sb, "unable to read map");
-			goto error_free;
-		}
+	ret = adfs_map_read(dm, sb, map_addr, nzones);
+	if (ret) {
+		adfs_error(sb, "unable to read map");
+		goto error_free;
 	}
 
-	/* adjust the limits for the first and last map zones */
-	i = zone - 1;
-	dm[0].dm_startblk = 0;
-	dm[0].dm_startbit = ADFS_DR_SIZE_BITS;
-	dm[i].dm_endbit   = (adfs_disc_size(dr) >> dr->log2bpmb) +
-			    (ADFS_DR_SIZE_BITS - i * zone_size);
-
 	if (adfs_checkmap(sb, dm))
 		return dm;
 
 	adfs_error(sb, "map corrupted");
 
 error_free:
-	while (--zone >= 0)
-		brelse(dm[zone].dm_bh);
-
+	adfs_map_relse(dm, nzones);
 	kfree(dm);
 	return ERR_PTR(-EIO);
 }
Signature · 1 line
2.20.1
#11 lore raw

PATCH 6/41 fs/adfs: map: factor out map cleanup

In reply to Russell King - ARM Linux admin

We have several places which deal with releasing the map buffers and freeing the map array. Provide a helper for this.

---
 fs/adfs/adfs.h  |  1 +
 fs/adfs/map.c   |  8 ++++++++
 fs/adfs/super.c | 10 ++--------
 3 files changed, 11 insertions(+), 8 deletions(-)
fs/adfs/adfs.h +1 -0
index 45fd48fbd5e0..6497da8a2c8a 100644
@@ -147,6 +147,7 @@ int adfs_notify_change(struct dentry *dentry, struct iattr *attr);
 int adfs_map_lookup(struct super_block *sb, u32 frag_id, unsigned int offset);
 void adfs_map_statfs(struct super_block *sb, struct kstatfs *buf);
 struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecord *dr);
+void adfs_free_map(struct super_block *sb);
 
 /* Misc */
 __printf(3, 4)
fs/adfs/map.c +8 -0
index 4b677cd5d015..8ba8877110ff 100644
@@ -421,3 +421,11 @@ struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecor
 	kfree(dm);
 	return ERR_PTR(-EIO);
 }
+
+void adfs_free_map(struct super_block *sb)
+{
+	struct adfs_sb_info *asb = ADFS_SB(sb);
+
+	adfs_map_relse(asb->s_map, asb->s_map_size);
+	kfree(asb->s_map);
+}
fs/adfs/super.c +2 -8
index 458824e0ca83..cef16028e9f2 100644
@@ -90,12 +90,9 @@ static int adfs_checkdiscrecord(struct adfs_discrecord *dr)
 
 static void adfs_put_super(struct super_block *sb)
 {
-	int i;
 	struct adfs_sb_info *asb = ADFS_SB(sb);
 
-	for (i = 0; i < asb->s_map_size; i++)
-		brelse(asb->s_map[i].dm_bh);
-	kfree(asb->s_map);
+	adfs_free_map(sb);
 	kfree_rcu(asb, rcu);
 }
 
@@ -412,10 +409,7 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent)
 	root = adfs_iget(sb, &root_obj);
 	sb->s_root = d_make_root(root);
 	if (!sb->s_root) {
-		int i;
-		for (i = 0; i < asb->s_map_size; i++)
-			brelse(asb->s_map[i].dm_bh);
-		kfree(asb->s_map);
+		adfs_free_map(sb);
 		adfs_error(sb, "get root inode failed\n");
 		ret = -EIO;
 		goto error;
Signature · 1 line
2.20.1
#12 lore raw

PATCH 7/41 fs/adfs: map: incorporate map offsets into layout

In reply to Russell King - ARM Linux admin

lookup_zone() and scan_free_map() cope in different ways with the location of the map data within a zone:

1. lookup_zone() adds a four byte offset to the map data pointer to skip over the check and free link bytes.

2. scan_free_map() needs to use the free link pointer, which is an offset from itself, so we end up adding a 32-bit offset to the end pointer (aka mapsize) which is really confusing.

Rename mapsize to endbit as this is really what it is, and incorporate the 32-bit offset into the map layout. This means that both dm_startbit and dm_endbit are now bit offsets from the start of the buffer, rather than four bytes in to the buffer.

---
 fs/adfs/map.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)
fs/adfs/map.c +11 -11
index 8ba8877110ff..55bd7c20158c 100644
@@ -68,9 +68,9 @@ static DEFINE_RWLOCK(adfs_map_lock);
 static int lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
 		       const u32 frag_id, unsigned int *offset)
 {
-	const unsigned int mapsize = dm->dm_endbit;
+	const unsigned int endbit = dm->dm_endbit;
 	const u32 idmask = (1 << idlen) - 1;
-	unsigned char *map = dm->dm_bh->b_data + 4;
+	unsigned char *map = dm->dm_bh->b_data;
 	unsigned int start = dm->dm_startbit;
 	unsigned int mapptr;
 	u32 frag;
@@ -87,7 +87,7 @@ static int lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
 			u32 v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31);
 			while (v == 0) {
 				mapptr = (mapptr & ~31) + 32;
-				if (mapptr >= mapsize)
+				if (mapptr >= endbit)
 					goto error;
 				v = le32_to_cpu(_map[mapptr >> 5]);
 			}
@@ -99,7 +99,7 @@ static int lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
 			goto found;
 again:
 		start = mapptr;
-	} while (mapptr < mapsize);
+	} while (mapptr < endbit);
 	return -1;
 
 error:
@@ -127,7 +127,7 @@ static int lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
 static unsigned int
 scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm)
 {
-	const unsigned int mapsize = dm->dm_endbit + 32;
+	const unsigned int endbit = dm->dm_endbit;
 	const unsigned int idlen  = asb->s_idlen;
 	const unsigned int frag_idlen = idlen <= 15 ? idlen : 15;
 	const u32 idmask = (1 << frag_idlen) - 1;
@@ -165,7 +165,7 @@ scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm)
 			u32 v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31);
 			while (v == 0) {
 				mapptr = (mapptr & ~31) + 32;
-				if (mapptr >= mapsize)
+				if (mapptr >= endbit)
 					goto error;
 				v = le32_to_cpu(_map[mapptr >> 5]);
 			}
@@ -345,19 +345,19 @@ static void adfs_map_layout(struct adfs_discmap *dm, unsigned int nzones,
 
 	dm[0].dm_bh       = NULL;
 	dm[0].dm_startblk = 0;
-	dm[0].dm_startbit = ADFS_DR_SIZE_BITS;
-	dm[0].dm_endbit   = zone_size;
+	dm[0].dm_startbit = 32 + ADFS_DR_SIZE_BITS;
+	dm[0].dm_endbit   = 32 + zone_size;
 
 	for (zone = 1; zone < nzones; zone++) {
 		dm[zone].dm_bh       = NULL;
 		dm[zone].dm_startblk = zone * zone_size - ADFS_DR_SIZE_BITS;
-		dm[zone].dm_startbit = 0;
-		dm[zone].dm_endbit   = zone_size;
+		dm[zone].dm_startbit = 32;
+		dm[zone].dm_endbit   = 32 + zone_size;
 	}
 
 	size = adfs_disc_size(dr) >> dr->log2bpmb;
 	size -= (nzones - 1) * zone_size - ADFS_DR_SIZE_BITS;
-	dm[nzones - 1].dm_endbit = size;
+	dm[nzones - 1].dm_endbit = 32 + size;
 }
 
 static int adfs_map_read(struct adfs_discmap *dm, struct super_block *sb,
Signature · 1 line
2.20.1
#13 lore raw

PATCH 8/41 fs/adfs: map: use find_next_bit_le() rather than open coding it

In reply to Russell King - ARM Linux admin

Use find_next_bit_le() to find the end of a fragment in the map rather than open-coding this functionality.

---
 fs/adfs/map.c | 76 ++++++++++++++-------------------------------------
 1 file changed, 21 insertions(+), 55 deletions(-)
fs/adfs/map.c +21 -55
index 55bd7c20158c..9be0b47da19c 100644
@@ -72,50 +72,32 @@ static int lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
 	const u32 idmask = (1 << idlen) - 1;
 	unsigned char *map = dm->dm_bh->b_data;
 	unsigned int start = dm->dm_startbit;
-	unsigned int mapptr;
+	unsigned int fragend;
 	u32 frag;
 
 	do {
 		frag = GET_FRAG_ID(map, start, idmask);
-		mapptr = start + idlen;
-
-		/*
-		 * find end of fragment
-		 */
-		{
-			__le32 *_map = (__le32 *)map;
-			u32 v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31);
-			while (v == 0) {
-				mapptr = (mapptr & ~31) + 32;
-				if (mapptr >= endbit)
-					goto error;
-				v = le32_to_cpu(_map[mapptr >> 5]);
-			}
-
-			mapptr += 1 + ffz(~v);
+
+		fragend = find_next_bit_le(map, endbit, start + idlen);
+		if (fragend >= endbit)
+			goto error;
+
+		if (frag == frag_id) {
+			unsigned int length = fragend + 1 - start;
+
+			if (*offset < length)
+				return start + *offset;
+			*offset -= length;
 		}
 
-		if (frag == frag_id)
-			goto found;
-again:
-		start = mapptr;
-	} while (mapptr < endbit);
+		start = fragend + 1;
+	} while (start < endbit);
 	return -1;
 
 error:
 	printk(KERN_ERR "adfs: oversized fragment 0x%x at 0x%x-0x%x\n",
-		frag, start, mapptr);
+		frag, start, fragend);
 	return -1;
-
-found:
-	{
-		int length = mapptr - start;
-		if (*offset >= length) {
-			*offset -= length;
-			goto again;
-		}
-	}
-	return start + *offset;
 }
 
 /*
@@ -132,7 +114,7 @@ scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm)
 	const unsigned int frag_idlen = idlen <= 15 ? idlen : 15;
 	const u32 idmask = (1 << frag_idlen) - 1;
 	unsigned char *map = dm->dm_bh->b_data;
-	unsigned int start = 8, mapptr;
+	unsigned int start = 8, fragend;
 	u32 frag;
 	unsigned long total = 0;
 
@@ -151,29 +133,13 @@ scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm)
 	do {
 		start += frag;
 
-		/*
-		 * get fragment id
-		 */
 		frag = GET_FRAG_ID(map, start, idmask);
-		mapptr = start + idlen;
-
-		/*
-		 * find end of fragment
-		 */
-		{
-			__le32 *_map = (__le32 *)map;
-			u32 v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31);
-			while (v == 0) {
-				mapptr = (mapptr & ~31) + 32;
-				if (mapptr >= endbit)
-					goto error;
-				v = le32_to_cpu(_map[mapptr >> 5]);
-			}
-
-			mapptr += 1 + ffz(~v);
-		}
 
-		total += mapptr - start;
+		fragend = find_next_bit_le(map, endbit, start + idlen);
+		if (fragend >= endbit)
+			goto error;
+
+		total += fragend + 1 - start;
 	} while (frag >= idlen + 1);
 
 	if (frag != 0)
Signature · 1 line
2.20.1
#14 lore raw

PATCH 9/41 fs/adfs: map: move map-specific sb initialisation to map.c

In reply to Russell King - ARM Linux admin

Move map specific superblock initialisation to map.c, rather than having it spread into super.c.

---
 fs/adfs/map.c   | 13 +++++++++----
 fs/adfs/super.c |  7 +------
 2 files changed, 10 insertions(+), 10 deletions(-)
fs/adfs/map.c +9 -4
index 9be0b47da19c..82e1bf101fe6 100644
@@ -355,14 +355,19 @@ struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecor
 	unsigned int map_addr, zone_size, nzones;
 	int ret;
 
-	nzones    = asb->s_map_size;
+	nzones    = dr->nzones | dr->nzones_high << 8;
 	zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare);
-	map_addr  = (nzones >> 1) * zone_size -
-		     ((nzones > 1) ? ADFS_DR_SIZE_BITS : 0);
-	map_addr  = signed_asl(map_addr, asb->s_map2blk);
 
+	asb->s_idlen = dr->idlen;
+	asb->s_map_size = nzones;
+	asb->s_map2blk = dr->log2bpmb - dr->log2secsize;
+	asb->s_log2sharesize = dr->log2sharesize;
 	asb->s_ids_per_zone = zone_size / (asb->s_idlen + 1);
 
+	map_addr = (nzones >> 1) * zone_size -
+		     ((nzones > 1) ? ADFS_DR_SIZE_BITS : 0);
+	map_addr = signed_asl(map_addr, asb->s_map2blk);
+
 	dm = kmalloc_array(nzones, sizeof(*dm), GFP_KERNEL);
 	if (dm == NULL) {
 		adfs_error(sb, "not enough memory");
fs/adfs/super.c +1 -6
index cef16028e9f2..b2455e9ab923 100644
@@ -289,6 +289,7 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent)
 		return -ENOMEM;
 
 	sb->s_fs_info = asb;
+	sb->s_magic = ADFS_SUPER_MAGIC;
 	sb->s_time_gran = 10000000;
 
 	/* set default options */
@@ -356,12 +357,6 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent)
 	 * blocksize on this device should now be set to the ADFS log2secsize
 	 */
 
-	sb->s_magic		= ADFS_SUPER_MAGIC;
-	asb->s_idlen		= dr->idlen;
-	asb->s_map_size		= dr->nzones | (dr->nzones_high << 8);
-	asb->s_map2blk		= dr->log2bpmb - dr->log2secsize;
-	asb->s_log2sharesize	= dr->log2sharesize;
-
 	asb->s_map = adfs_read_map(sb, dr);
 	if (IS_ERR(asb->s_map)) {
 		ret =  PTR_ERR(asb->s_map);
Signature · 1 line
2.20.1
#15 lore raw

PATCH 10/41 fs/adfs: map: fix map scanning

In reply to Russell King - ARM Linux admin

When scanning the map for a fragment id, we need to keep track of the free space links, so we don't inadvertently believe that the freespace link is a valid fragment id.

---
 fs/adfs/map.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
fs/adfs/map.c +7 -2
index 82e1bf101fe6..a81de80c45c1 100644
@@ -72,9 +72,12 @@ static int lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
 	const u32 idmask = (1 << idlen) - 1;
 	unsigned char *map = dm->dm_bh->b_data;
 	unsigned int start = dm->dm_startbit;
-	unsigned int fragend;
+	unsigned int freelink, fragend;
 	u32 frag;
 
+	frag = GET_FRAG_ID(map, 8, idmask & 0x7fff);
+	freelink = frag ? 8 + frag : 0;
+
 	do {
 		frag = GET_FRAG_ID(map, start, idmask);
 
@@ -82,7 +85,9 @@ static int lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
 		if (fragend >= endbit)
 			goto error;
 
-		if (frag == frag_id) {
+		if (start == freelink) {
+			freelink += frag & 0x7fff;
+		} else if (frag == frag_id) {
 			unsigned int length = fragend + 1 - start;
 
 			if (*offset < length)
Signature · 1 line
2.20.1
#16 lore raw

PATCH 11/41 fs/adfs: dir: rename bh_fplus to bhs

In reply to Russell King - ARM Linux admin

Rename bh_fplus to bhs in preparation to make some of the directory handling code sharable between implementations.

---
 fs/adfs/adfs.h      |  4 +---
 fs/adfs/dir_fplus.c | 54 ++++++++++++++++++++++-----------------------
 2 files changed, 28 insertions(+), 30 deletions(-)
fs/adfs/adfs.h +1 -3
index 6497da8a2c8a..956ac0bd53e1 100644
@@ -93,9 +93,7 @@ struct adfs_dir {
 
 	int			nr_buffers;
 	struct buffer_head	*bh[4];
-
-	/* big directories need allocated buffers */
-	struct buffer_head	**bh_fplus;
+	struct buffer_head	**bhs;
 
 	unsigned int		pos;
 	__u32			parent_id;
fs/adfs/dir_fplus.c +27 -27

147 lines not on this page · raw message

Signature · 1 line
2.20.1
#17 lore raw

PATCH 12/41 fs/adfs: dir: add common dir object initialisation

In reply to Russell King - ARM Linux admin

Initialise the dir object before we pass it down to the directory format specific read handler. This allows us to get rid of the initialisation inside those handlers.

---
 fs/adfs/dir.c       | 16 +++++++++++++---
 fs/adfs/dir_f.c     |  3 ---
 fs/adfs/dir_fplus.c |  6 ------
 3 files changed, 13 insertions(+), 12 deletions(-)
fs/adfs/dir.c +13 -3
index a54c53244992..c1b8b5bccbec 100644
@@ -13,6 +13,16 @@
  */
 static DEFINE_RWLOCK(adfs_dir_lock);
 
+static int adfs_dir_read(struct super_block *sb, u32 indaddr,
+			 unsigned int size, struct adfs_dir *dir)
+{
+	dir->sb = sb;
+	dir->bhs = dir->bh;
+	dir->nr_buffers = 0;
+
+	return ADFS_SB(sb)->s_dir->read(sb, indaddr, size, dir);
+}
+
 void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
 {
 	unsigned int dots, i;
@@ -64,7 +74,7 @@ adfs_readdir(struct file *file, struct dir_context *ctx)
 	if (ctx->pos >> 32)
 		return 0;
 
-	ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
+	ret = adfs_dir_read(sb, inode->i_ino, inode->i_size, &dir);
 	if (ret)
 		return ret;
 
@@ -115,7 +125,7 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
 		goto out;
 	}
 
-	ret = ops->read(sb, obj->parent_id, 0, &dir);
+	ret = adfs_dir_read(sb, obj->parent_id, 0, &dir);
 	if (ret)
 		goto out;
 
@@ -167,7 +177,7 @@ static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
 	u32 name_len;
 	int ret;
 
-	ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
+	ret = adfs_dir_read(sb, inode->i_ino, inode->i_size, &dir);
 	if (ret)
 		goto out;
 
fs/adfs/dir_f.c +0 -3
index c1a950c7400a..e62f35eb7789 100644
@@ -139,9 +139,6 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr,
 
 	size >>= blocksize_bits;
 
-	dir->nr_buffers = 0;
-	dir->sb = sb;
-
 	for (blk = 0; blk < size; blk++) {
 		int phys;
 
fs/adfs/dir_fplus.c +0 -6
index 5f5420c9b943..52c42a9986d9 100644
@@ -17,11 +17,6 @@ adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct
 	unsigned int blk, size;
 	int i, ret = -EIO;
 
-	dir->nr_buffers = 0;
-
-	/* start off using fixed bh set - only alloc for big dirs */
-	dir->bhs = &dir->bh[0];
-
 	block = __adfs_block_map(sb, id, 0);
 	if (!block) {
 		adfs_error(sb, "dir object %X has a hole at offset 0", id);
@@ -94,7 +89,6 @@ adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct
 	}
 
 	dir->parent_id = le32_to_cpu(h->bigdirparent);
-	dir->sb = sb;
 	return 0;
 
 out:
Signature · 1 line
2.20.1
#18 lore raw

PATCH 13/41 fs/adfs: dir: add common directory buffer release method

In reply to Russell King - ARM Linux admin

With the bhs pointer in place, we have no need for separate per-format free() methods, since a generic version will do. Provide a generic implementation, remove the format specific implementations and the method function pointer.

---
 fs/adfs/adfs.h      |  2 +-
 fs/adfs/dir.c       | 21 ++++++++++++++++++---
 fs/adfs/dir_f.c     | 28 ++++------------------------
 fs/adfs/dir_fplus.c | 34 ++--------------------------------
 4 files changed, 25 insertions(+), 60 deletions(-)
fs/adfs/adfs.h +1 -1
index 956ac0bd53e1..3bb6fd5b5eb0 100644
@@ -126,7 +126,6 @@ struct adfs_dir_ops {
 	int	(*create)(struct adfs_dir *dir, struct object_info *obj);
 	int	(*remove)(struct adfs_dir *dir, struct object_info *obj);
 	int	(*sync)(struct adfs_dir *dir);
-	void	(*free)(struct adfs_dir *dir);
 };
 
 struct adfs_discmap {
@@ -167,6 +166,7 @@ extern const struct dentry_operations adfs_dentry_operations;
 extern const struct adfs_dir_ops adfs_f_dir_ops;
 extern const struct adfs_dir_ops adfs_fplus_dir_ops;
 
+void adfs_dir_relse(struct adfs_dir *dir);
 void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj);
 extern int adfs_dir_update(struct super_block *sb, struct object_info *obj,
 			   int wait);
fs/adfs/dir.c +18 -3
index c1b8b5bccbec..f50302775504 100644
@@ -6,6 +6,7 @@
  *
  *  Common directory handling for ADFS
  */
+#include <linux/slab.h>
 #include "adfs.h"
 
 /*
@@ -13,6 +14,20 @@
  */
 static DEFINE_RWLOCK(adfs_dir_lock);
 
+void adfs_dir_relse(struct adfs_dir *dir)
+{
+	unsigned int i;
+
+	for (i = 0; i < dir->nr_buffers; i++)
+		brelse(dir->bhs[i]);
+	dir->nr_buffers = 0;
+
+	if (dir->bhs != dir->bh)
+		kfree(dir->bhs);
+	dir->bhs = NULL;
+	dir->sb = NULL;
+}
+
 static int adfs_dir_read(struct super_block *sb, u32 indaddr,
 			 unsigned int size, struct adfs_dir *dir)
 {
@@ -105,7 +120,7 @@ adfs_readdir(struct file *file, struct dir_context *ctx)
 	read_unlock(&adfs_dir_lock);
 
 free_out:
-	ops->free(&dir);
+	adfs_dir_relse(&dir);
 	return ret;
 }
 
@@ -139,7 +154,7 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
 			ret = err;
 	}
 
-	ops->free(&dir);
+	adfs_dir_relse(&dir);
 out:
 #endif
 	return ret;
@@ -211,7 +226,7 @@ static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
 	read_unlock(&adfs_dir_lock);
 
 free_out:
-	ops->free(&dir);
+	adfs_dir_relse(&dir);
 out:
 	return ret;
 }
fs/adfs/dir_f.c +4 -24
index e62f35eb7789..e249fdb915fa 100644
@@ -9,8 +9,6 @@
 #include "adfs.h"
 #include "dir_f.h"
 
-static void adfs_f_free(struct adfs_dir *dir);
-
 /*
  * Read an (unaligned) value of length 1..4 bytes
  */
@@ -128,7 +126,7 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr,
 			 unsigned int size, struct adfs_dir *dir)
 {
 	const unsigned int blocksize_bits = sb->s_blocksize_bits;
-	int blk = 0;
+	int blk;
 
 	/*
 	 * Directories which are not a multiple of 2048 bytes
@@ -152,6 +150,8 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr,
 		dir->bh[blk] = sb_bread(sb, phys);
 		if (!dir->bh[blk])
 			goto release_buffers;
+
+		dir->nr_buffers += 1;
 	}
 
 	memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead));
@@ -168,17 +168,12 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr,
 	if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte)
 		goto bad_dir;
 
-	dir->nr_buffers = blk;
-
 	return 0;
 
 bad_dir:
 	adfs_error(sb, "dir %06x is corrupted", indaddr);
 release_buffers:
-	for (blk -= 1; blk >= 0; blk -= 1)
-		brelse(dir->bh[blk]);
-
-	dir->sb = NULL;
+	adfs_dir_relse(dir);
 
 	return -EIO;
 }
@@ -435,25 +430,10 @@ adfs_f_sync(struct adfs_dir *dir)
 	return err;
 }
 
-static void
-adfs_f_free(struct adfs_dir *dir)
-{
-	int i;
-
-	for (i = dir->nr_buffers - 1; i >= 0; i--) {
-		brelse(dir->bh[i]);
-		dir->bh[i] = NULL;
-	}
-
-	dir->nr_buffers = 0;
-	dir->sb = NULL;
-}
-
 const struct adfs_dir_ops adfs_f_dir_ops = {
 	.read		= adfs_f_read,
 	.setpos		= adfs_f_setpos,
 	.getnext	= adfs_f_getnext,
 	.update		= adfs_f_update,
 	.sync		= adfs_f_sync,
-	.free		= adfs_f_free
 };
fs/adfs/dir_fplus.c +2 -32
index 52c42a9986d9..25308b334dd3 100644
@@ -15,7 +15,7 @@ adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct
 	struct adfs_bigdirtail *t;
 	unsigned long block;
 	unsigned int blk, size;
-	int i, ret = -EIO;
+	int ret = -EIO;
 
 	block = __adfs_block_map(sb, id, 0);
 	if (!block) {
@@ -92,18 +92,8 @@ adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct
 	return 0;
 
 out:
-	if (dir->bhs) {
-		for (i = 0; i < dir->nr_buffers; i++)
-			brelse(dir->bhs[i]);
+	adfs_dir_relse(dir);
 
-		if (&dir->bh[0] != dir->bhs)
-			kfree(dir->bhs);
-
-		dir->bhs = NULL;
-	}
-
-	dir->nr_buffers = 0;
-	dir->sb = NULL;
 	return ret;
 }
 
@@ -205,29 +195,9 @@ adfs_fplus_sync(struct adfs_dir *dir)
 	return err;
 }
 
-static void
-adfs_fplus_free(struct adfs_dir *dir)
-{
-	int i;
-
-	if (dir->bhs) {
-		for (i = 0; i < dir->nr_buffers; i++)
-			brelse(dir->bhs[i]);
-
-		if (&dir->bh[0] != dir->bhs)
-			kfree(dir->bhs);
-
-		dir->bhs = NULL;
-	}
-
-	dir->nr_buffers = 0;
-	dir->sb = NULL;
-}
-
 const struct adfs_dir_ops adfs_fplus_dir_ops = {
 	.read		= adfs_fplus_read,
 	.setpos		= adfs_fplus_setpos,
 	.getnext	= adfs_fplus_getnext,
 	.sync		= adfs_fplus_sync,
-	.free		= adfs_fplus_free
 };
Signature · 1 line
2.20.1
#19 lore raw

PATCH 14/41 fs/adfs: dir: add common directory sync method

In reply to Russell King - ARM Linux admin

adfs_fplus_sync() can be used for both directory formats since we now have a common way to access the buffer heads, so move it into dir.c and appropriately rename it. Remove the directory-format specific implementations.

---
 fs/adfs/adfs.h      |  1 -
 fs/adfs/dir.c       | 23 ++++++++++++++++++-----
 fs/adfs/dir_f.c     | 17 -----------------
 fs/adfs/dir_fplus.c | 17 -----------------
 4 files changed, 18 insertions(+), 40 deletions(-)
fs/adfs/adfs.h +0 -1
index 3bb6fd5b5eb0..5f1acee768f5 100644
@@ -125,7 +125,6 @@ struct adfs_dir_ops {
 	int	(*update)(struct adfs_dir *dir, struct object_info *obj);
 	int	(*create)(struct adfs_dir *dir, struct object_info *obj);
 	int	(*remove)(struct adfs_dir *dir, struct object_info *obj);
-	int	(*sync)(struct adfs_dir *dir);
 };
 
 struct adfs_discmap {
fs/adfs/dir.c +18 -5
index f50302775504..16a2639d3ca5 100644
@@ -38,6 +38,21 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr,
 	return ADFS_SB(sb)->s_dir->read(sb, indaddr, size, dir);
 }
 
+static int adfs_dir_sync(struct adfs_dir *dir)
+{
+	int err = 0;
+	int i;
+
+	for (i = dir->nr_buffers - 1; i >= 0; i--) {
+		struct buffer_head *bh = dir->bhs[i];
+		sync_dirty_buffer(bh);
+		if (buffer_req(bh) && !buffer_uptodate(bh))
+			err = -EIO;
+	}
+
+	return err;
+}
+
 void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
 {
 	unsigned int dots, i;
@@ -135,10 +150,8 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
 	printk(KERN_INFO "adfs_dir_update: object %06x in dir %06x\n",
 		 obj->indaddr, obj->parent_id);
 
-	if (!ops->update) {
-		ret = -EINVAL;
-		goto out;
-	}
+	if (!ops->update)
+		return -EINVAL;
 
 	ret = adfs_dir_read(sb, obj->parent_id, 0, &dir);
 	if (ret)
@@ -149,7 +162,7 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
 	write_unlock(&adfs_dir_lock);
 
 	if (wait) {
-		int err = ops->sync(&dir);
+		int err = adfs_dir_sync(&dir);
 		if (!ret)
 			ret = err;
 	}
fs/adfs/dir_f.c +0 -17
index e249fdb915fa..80ac261b9ec4 100644
@@ -414,26 +414,9 @@ adfs_f_update(struct adfs_dir *dir, struct object_info *obj)
 #endif
 }
 
-static int
-adfs_f_sync(struct adfs_dir *dir)
-{
-	int err = 0;
-	int i;
-
-	for (i = dir->nr_buffers - 1; i >= 0; i--) {
-		struct buffer_head *bh = dir->bh[i];
-		sync_dirty_buffer(bh);
-		if (buffer_req(bh) && !buffer_uptodate(bh))
-			err = -EIO;
-	}
-
-	return err;
-}
-
 const struct adfs_dir_ops adfs_f_dir_ops = {
 	.read		= adfs_f_read,
 	.setpos		= adfs_f_setpos,
 	.getnext	= adfs_f_getnext,
 	.update		= adfs_f_update,
-	.sync		= adfs_f_sync,
 };
fs/adfs/dir_fplus.c +0 -17
index 25308b334dd3..1196c8962feb 100644
@@ -179,25 +179,8 @@ adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
 	return ret;
 }
 
-static int
-adfs_fplus_sync(struct adfs_dir *dir)
-{
-	int err = 0;
-	int i;
-
-	for (i = dir->nr_buffers - 1; i >= 0; i--) {
-		struct buffer_head *bh = dir->bhs[i];
-		sync_dirty_buffer(bh);
-		if (buffer_req(bh) && !buffer_uptodate(bh))
-			err = -EIO;
-	}
-
-	return err;
-}
-
 const struct adfs_dir_ops adfs_fplus_dir_ops = {
 	.read		= adfs_fplus_read,
 	.setpos		= adfs_fplus_setpos,
 	.getnext	= adfs_fplus_getnext,
-	.sync		= adfs_fplus_sync,
 };
Signature · 1 line
2.20.1
#20 lore raw

PATCH 15/41 fs/adfs: dir: add generic copy functions

In reply to Russell King - ARM Linux admin

Directories can span multiple buffers, and we currently open-code memcpy access to these buffers, including dealing with entries that are split across multiple buffers. Such code exists in both directory format implementations.

Provide common functions to allow data to be copied from/to the directory buffers as if they were a contiguous set of buffers, and use them when accessing directories.

---
 fs/adfs/adfs.h      |  4 ++++
 fs/adfs/dir.c       | 50 ++++++++++++++++++++++++++++++++++++++++
 fs/adfs/dir_f.c     | 56 ++++++++-------------------------------------
 fs/adfs/dir_fplus.c | 47 ++++++++++---------------------------
 4 files changed, 75 insertions(+), 82 deletions(-)
fs/adfs/adfs.h +4 -0
index 5f1acee768f5..92cbc4b1d902 100644
@@ -165,6 +165,10 @@ extern const struct dentry_operations adfs_dentry_operations;
 extern const struct adfs_dir_ops adfs_f_dir_ops;
 extern const struct adfs_dir_ops adfs_fplus_dir_ops;
 
+int adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset,
+		      size_t len);
+int adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src,
+		    size_t len);
 void adfs_dir_relse(struct adfs_dir *dir);
 void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj);
 extern int adfs_dir_update(struct super_block *sb, struct object_info *obj,
fs/adfs/dir.c +50 -0

56 lines not on this page · raw message

fs/adfs/dir_f.c +9 -47

75 lines not on this page · raw message

fs/adfs/dir_fplus.c +12 -35

75 lines not on this page · raw message

Signature · 1 line
2.20.1
#21 lore raw

PATCH 16/41 fs/adfs: dir: add generic directory reading

In reply to Russell King - ARM Linux admin

Both directory formats code the mechanics of fetching the directory buffers using their own implementations. Consolidate these into one implementation.

---
 fs/adfs/adfs.h      |  2 ++
 fs/adfs/dir.c       | 49 +++++++++++++++++++++++++++++
 fs/adfs/dir_f.c     | 24 +++-----------
 fs/adfs/dir_fplus.c | 76 ++++++++++++---------------------------------
 4 files changed, 74 insertions(+), 77 deletions(-)
fs/adfs/adfs.h +2 -0
index 92cbc4b1d902..01d065937c01 100644
@@ -170,6 +170,8 @@ int adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset,
 int adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src,
 		    size_t len);
 void adfs_dir_relse(struct adfs_dir *dir);
+int adfs_dir_read_buffers(struct super_block *sb, u32 indaddr,
+			  unsigned int size, struct adfs_dir *dir);
 void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj);
 extern int adfs_dir_update(struct super_block *sb, struct object_info *obj,
 			   int wait);
fs/adfs/dir.c +49 -0

55 lines not on this page · raw message

fs/adfs/dir_f.c +4 -20
index 3c3b423577d2..027ee714f42b 100644
@@ -126,7 +126,7 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr,
 			 unsigned int size, struct adfs_dir *dir)
 {
 	const unsigned int blocksize_bits = sb->s_blocksize_bits;
-	int blk;
+	int ret;
 
 	/*
 	 * Directories which are not a multiple of 2048 bytes
@@ -135,24 +135,9 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr,
 	if (size & 2047)
 		goto bad_dir;
 
-	size >>= blocksize_bits;
-
-	for (blk = 0; blk < size; blk++) {
-		int phys;
-
-		phys = __adfs_block_map(sb, indaddr, blk);
-		if (!phys) {
-			adfs_error(sb, "dir %06x has a hole at offset %d",
-				   indaddr, blk);
-			goto release_buffers;
-		}
-
-		dir->bh[blk] = sb_bread(sb, phys);
-		if (!dir->bh[blk])
-			goto release_buffers;
-
-		dir->nr_buffers += 1;
-	}
+	ret = adfs_dir_read_buffers(sb, indaddr, size, dir);
+	if (ret)
+		return ret;
 
 	memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead));
 	memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail));
@@ -172,7 +157,6 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr,
 
 bad_dir:
 	adfs_error(sb, "dir %06x is corrupted", indaddr);
-release_buffers:
 	adfs_dir_relse(dir);
 
 	return -EIO;
fs/adfs/dir_fplus.c +19 -57

106 lines not on this page · raw message

Signature · 1 line
2.20.1
#22 lore raw

PATCH 17/41 fs/adfs: dir: add helper to read directory using inode

In reply to Russell King - ARM Linux admin

Add a helper to read a directory using the inode, which we do in two places.

---
 fs/adfs/dir.c | 32 ++++++++++++++++++++++----------
 1 file changed, 22 insertions(+), 10 deletions(-)
fs/adfs/dir.c +22 -10
index b8e2a909fa3f..882377e86041 100644
@@ -137,6 +137,26 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr,
 	return ADFS_SB(sb)->s_dir->read(sb, indaddr, size, dir);
 }
 
+static int adfs_dir_read_inode(struct super_block *sb, struct inode *inode,
+			       struct adfs_dir *dir)
+{
+	int ret;
+
+	ret = adfs_dir_read(sb, inode->i_ino, inode->i_size, dir);
+	if (ret)
+		return ret;
+
+	if (ADFS_I(inode)->parent_id != dir->parent_id) {
+		adfs_error(sb,
+			   "parent directory id changed under me! (%06x but got %06x)\n",
+			   ADFS_I(inode)->parent_id, dir->parent_id);
+		adfs_dir_relse(dir);
+		ret = -EIO;
+	}
+
+	return ret;
+}
+
 static int adfs_dir_sync(struct adfs_dir *dir)
 {
 	int err = 0;
@@ -203,7 +223,7 @@ adfs_readdir(struct file *file, struct dir_context *ctx)
 	if (ctx->pos >> 32)
 		return 0;
 
-	ret = adfs_dir_read(sb, inode->i_ino, inode->i_size, &dir);
+	ret = adfs_dir_read_inode(sb, inode, &dir);
 	if (ret)
 		return ret;
 
@@ -304,18 +324,10 @@ static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
 	u32 name_len;
 	int ret;
 
-	ret = adfs_dir_read(sb, inode->i_ino, inode->i_size, &dir);
+	ret = adfs_dir_read_inode(sb, inode, &dir);
 	if (ret)
 		goto out;
 
-	if (ADFS_I(inode)->parent_id != dir.parent_id) {
-		adfs_error(sb,
-			   "parent directory changed under me! (%06x but got %06x)\n",
-			   ADFS_I(inode)->parent_id, dir.parent_id);
-		ret = -EIO;
-		goto free_out;
-	}
-
 	obj->parent_id = inode->i_ino;
 
 	read_lock(&adfs_dir_lock);
Signature · 1 line
2.20.1
#23 lore raw

PATCH 18/41 fs/adfs: dir: add helper to mark directory buffers dirty

In reply to Russell King - ARM Linux admin

Provide a helper for marking directory buffers dirty so they get written back to disk.

---
 fs/adfs/dir.c   | 12 ++++++++++++
 fs/adfs/dir_f.c |  5 +----
 2 files changed, 13 insertions(+), 4 deletions(-)
fs/adfs/dir.c +12 -0
index 882377e86041..e8aafc65d545 100644
@@ -157,6 +157,15 @@ static int adfs_dir_read_inode(struct super_block *sb, struct inode *inode,
 	return ret;
 }
 
+static void adfs_dir_mark_dirty(struct adfs_dir *dir)
+{
+	unsigned int i;
+
+	/* Mark the buffers dirty */
+	for (i = 0; i < dir->nr_buffers; i++)
+		mark_buffer_dirty(dir->bhs[i]);
+}
+
 static int adfs_dir_sync(struct adfs_dir *dir)
 {
 	int err = 0;
@@ -280,6 +289,9 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
 	ret = ops->update(&dir, obj);
 	write_unlock(&adfs_dir_lock);
 
+	if (ret == 0)
+		adfs_dir_mark_dirty(&dir);
+
 	if (wait) {
 		int err = adfs_dir_sync(&dir);
 		if (!ret)
fs/adfs/dir_f.c +1 -4
index 027ee714f42b..682df46d8d33 100644
@@ -306,7 +306,7 @@ static int
 adfs_f_update(struct adfs_dir *dir, struct object_info *obj)
 {
 	struct super_block *sb = dir->sb;
-	int ret, i;
+	int ret;
 
 	ret = adfs_dir_find_entry(dir, obj->indaddr);
 	if (ret < 0) {
@@ -347,9 +347,6 @@ adfs_f_update(struct adfs_dir *dir, struct object_info *obj)
 		goto bad_dir;
 	}
 #endif
-	for (i = dir->nr_buffers - 1; i >= 0; i--)
-		mark_buffer_dirty(dir->bh[i]);
-
 	ret = 0;
 out:
 	return ret;
Signature · 1 line
2.20.1
#24 lore raw

PATCH 19/41 fs/adfs: dir: update directory locking

In reply to Russell King - ARM Linux admin

Update directory locking such that it covers the validation of the directory, which could fail if another thread is concurrently writing to the same directory. Since we may sleep, we need to use a rwsem rather than a rw spinlock.

---
 fs/adfs/dir.c | 55 +++++++++++++++++++++++++++------------------------
 1 file changed, 29 insertions(+), 26 deletions(-)
fs/adfs/dir.c +29 -26
index e8aafc65d545..ff9c921be31c 100644
@@ -12,7 +12,7 @@
 /*
  * For future.  This should probably be per-directory.
  */
-static DEFINE_RWLOCK(adfs_dir_lock);
+static DECLARE_RWSEM(adfs_dir_rwsem);
 
 int adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset,
 		      size_t len)
@@ -232,26 +232,25 @@ adfs_readdir(struct file *file, struct dir_context *ctx)
 	if (ctx->pos >> 32)
 		return 0;
 
+	down_read(&adfs_dir_rwsem);
 	ret = adfs_dir_read_inode(sb, inode, &dir);
 	if (ret)
-		return ret;
+		goto unlock;
 
 	if (ctx->pos == 0) {
 		if (!dir_emit_dot(file, ctx))
-			goto free_out;
+			goto unlock_relse;
 		ctx->pos = 1;
 	}
 	if (ctx->pos == 1) {
 		if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))
-			goto free_out;
+			goto unlock_relse;
 		ctx->pos = 2;
 	}
 
-	read_lock(&adfs_dir_lock);
-
 	ret = ops->setpos(&dir, ctx->pos - 2);
 	if (ret)
-		goto unlock_out;
+		goto unlock_relse;
 	while (ops->getnext(&dir, &obj) == 0) {
 		if (!dir_emit(ctx, obj.name, obj.name_len,
 			      obj.indaddr, DT_UNKNOWN))
@@ -259,12 +258,14 @@ adfs_readdir(struct file *file, struct dir_context *ctx)
 		ctx->pos++;
 	}
 
-unlock_out:
-	read_unlock(&adfs_dir_lock);
-
-free_out:
+unlock_relse:
+	up_read(&adfs_dir_rwsem);
 	adfs_dir_relse(&dir);
 	return ret;
+
+unlock:
+	up_read(&adfs_dir_rwsem);
+	return ret;
 }
 
 int
@@ -281,13 +282,13 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
 	if (!ops->update)
 		return -EINVAL;
 
+	down_write(&adfs_dir_rwsem);
 	ret = adfs_dir_read(sb, obj->parent_id, 0, &dir);
 	if (ret)
-		goto out;
+		goto unlock;
 
-	write_lock(&adfs_dir_lock);
 	ret = ops->update(&dir, obj);
-	write_unlock(&adfs_dir_lock);
+	up_write(&adfs_dir_rwsem);
 
 	if (ret == 0)
 		adfs_dir_mark_dirty(&dir);
@@ -299,7 +300,10 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
 	}
 
 	adfs_dir_relse(&dir);
-out:
+	return ret;
+
+unlock:
+	up_write(&adfs_dir_rwsem);
 #endif
 	return ret;
 }
@@ -336,17 +340,14 @@ static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
 	u32 name_len;
 	int ret;
 
+	down_read(&adfs_dir_rwsem);
 	ret = adfs_dir_read_inode(sb, inode, &dir);
 	if (ret)
-		goto out;
-
-	obj->parent_id = inode->i_ino;
-
-	read_lock(&adfs_dir_lock);
+		goto unlock;
 
 	ret = ops->setpos(&dir, 0);
 	if (ret)
-		goto unlock_out;
+		goto unlock_relse;
 
 	ret = -ENOENT;
 	name = qstr->name;
@@ -357,13 +358,15 @@ static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
 			break;
 		}
 	}
+	obj->parent_id = inode->i_ino;
 
-unlock_out:
-	read_unlock(&adfs_dir_lock);
-
-free_out:
+unlock_relse:
+	up_read(&adfs_dir_rwsem);
 	adfs_dir_relse(&dir);
-out:
+	return ret;
+
+unlock:
+	up_read(&adfs_dir_rwsem);
 	return ret;
 }
 
Signature · 1 line
2.20.1
#25 lore raw

PATCH 20/41 fs/adfs: dir: modernise on-disk directory structures

In reply to Russell King - ARM Linux admin

Use __u8 and pack the structures for on-disk directories.

---
 fs/adfs/dir_f.h     | 52 ++++++++++++++++++++++++---------------------
 fs/adfs/dir_fplus.h |  6 +++---
 2 files changed, 31 insertions(+), 27 deletions(-)
fs/adfs/dir_f.h +28 -24
index 5aec332b90f5..a5393e6cf9f4 100644
@@ -13,9 +13,9 @@
  * Directory header
  */
 struct adfs_dirheader {
-	unsigned char startmasseq;
-	unsigned char startname[4];
-};
+	__u8 startmasseq;
+	__u8 startname[4];
+} __attribute__((packed));
 
 #define ADFS_NEWDIR_SIZE	2048
 #define ADFS_NUM_DIR_ENTRIES	77
@@ -31,32 +31,36 @@ struct adfs_direntry {
 	__u8 dirlen[4];
 	__u8 dirinddiscadd[3];
 	__u8 newdiratts;
-};
+} __attribute__((packed));
 
 /*
  * Directory tail
  */
+struct adfs_olddirtail {
+	__u8 dirlastmask;
+	char dirname[10];
+	__u8 dirparent[3];
+	char dirtitle[19];
+	__u8 reserved[14];
+	__u8 endmasseq;
+	__u8 endname[4];
+	__u8 dircheckbyte;
+} __attribute__((packed));
+
+struct adfs_newdirtail {
+	__u8 dirlastmask;
+	__u8 reserved[2];
+	__u8 dirparent[3];
+	char dirtitle[19];
+	char dirname[10];
+	__u8 endmasseq;
+	__u8 endname[4];
+	__u8 dircheckbyte;
+} __attribute__((packed));
+
 union adfs_dirtail {
-	struct {
-		unsigned char dirlastmask;
-		char dirname[10];
-		unsigned char dirparent[3];
-		char dirtitle[19];
-		unsigned char reserved[14];
-		unsigned char endmasseq;
-		unsigned char endname[4];
-		unsigned char dircheckbyte;
-	} old;
-	struct {
-		unsigned char dirlastmask;
-		unsigned char reserved[2];
-		unsigned char dirparent[3];
-		char dirtitle[19];
-		char dirname[10];
-		unsigned char endmasseq;
-		unsigned char endname[4];
-		unsigned char dircheckbyte;
-	} new;
+	struct adfs_olddirtail old;
+	struct adfs_newdirtail new;
 };
 
 #endif
fs/adfs/dir_fplus.h +3 -3
index 4ec0931e36ad..d729b1591e5e 100644
@@ -22,7 +22,7 @@ struct adfs_bigdirheader {
 	__le32	bigdirnamesize;
 	__le32	bigdirparent;
 	char	bigdirname[1];
-};
+} __attribute__((packed, aligned(4)));
 
 struct adfs_bigdirentry {
 	__le32	bigdirload;
@@ -32,11 +32,11 @@ struct adfs_bigdirentry {
 	__le32	bigdirattr;
 	__le32	bigdirobnamelen;
 	__le32	bigdirobnameptr;
-};
+} __attribute__((packed, aligned(4)));
 
 struct adfs_bigdirtail {
 	__le32	bigdirendname;
 	__u8	bigdirendmasseq;
 	__u8	reserved[2];
 	__u8	bigdircheckbyte;
-};
+} __attribute__((packed, aligned(4)));
Signature · 1 line
2.20.1
#26 lore raw

PATCH 21/41 fs/adfs: dir: improve update failure handling

In reply to Russell King - ARM Linux admin

When we update a directory, a number of errors may happen. If we failed to find the entry to update, we can just release the directory buffers as normal.

However, if we have some other error, we may have partially updated the buffers, resulting in an invalid directory. In this case, we need to discard the buffers to avoid writing the contents back to the media, and later re-read the directory from the media.

---
 fs/adfs/dir.c | 48 ++++++++++++++++++++++++++++++++++++------------
 1 file changed, 36 insertions(+), 12 deletions(-)
fs/adfs/dir.c +36 -12
index ff9c921be31c..5e5d344bae7c 100644
@@ -64,12 +64,8 @@ int adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src,
 	return 0;
 }
 
-void adfs_dir_relse(struct adfs_dir *dir)
+static void __adfs_dir_cleanup(struct adfs_dir *dir)
 {
-	unsigned int i;
-
-	for (i = 0; i < dir->nr_buffers; i++)
-		brelse(dir->bhs[i]);
 	dir->nr_buffers = 0;
 
 	if (dir->bhs != dir->bh)
@@ -78,6 +74,26 @@ void adfs_dir_relse(struct adfs_dir *dir)
 	dir->sb = NULL;
 }
 
+void adfs_dir_relse(struct adfs_dir *dir)
+{
+	unsigned int i;
+
+	for (i = 0; i < dir->nr_buffers; i++)
+		brelse(dir->bhs[i]);
+
+	__adfs_dir_cleanup(dir);
+}
+
+static void adfs_dir_forget(struct adfs_dir *dir)
+{
+	unsigned int i;
+
+	for (i = 0; i < dir->nr_buffers; i++)
+		bforget(dir->bhs[i]);
+
+	__adfs_dir_cleanup(dir);
+}
+
 int adfs_dir_read_buffers(struct super_block *sb, u32 indaddr,
 			  unsigned int size, struct adfs_dir *dir)
 {
@@ -288,20 +304,28 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
 		goto unlock;
 
 	ret = ops->update(&dir, obj);
+	if (ret)
+		goto forget;
 	up_write(&adfs_dir_rwsem);
 
-	if (ret == 0)
-		adfs_dir_mark_dirty(&dir);
+	adfs_dir_mark_dirty(&dir);
 
-	if (wait) {
-		int err = adfs_dir_sync(&dir);
-		if (!ret)
-			ret = err;
-	}
+	if (wait)
+		ret = adfs_dir_sync(&dir);
 
 	adfs_dir_relse(&dir);
 	return ret;
 
+	/*
+	 * If the updated failed because the entry wasn't found, we can
+	 * just release the buffers. If it was any other error, forget
+	 * the dirtied buffers so they aren't written back to the media.
+	 */
+forget:
+	if (ret == -ENOENT)
+		adfs_dir_relse(&dir);
+	else
+		adfs_dir_forget(&dir);
 unlock:
 	up_write(&adfs_dir_rwsem);
 #endif
Signature · 1 line
2.20.1
#27 lore raw

PATCH 22/41 fs/adfs: dir: improve compiler coverage in adfs_dir_update

In reply to Russell King - ARM Linux admin

Get rid of the ifdef, using IS_ENABLED() instead to detect whether the code should be callable. This allows the compiler to always parse the following code, reducing the chances of errors being missed.

---
 fs/adfs/dir.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
fs/adfs/dir.c +5 -3
index 5e5d344bae7c..931eefb2375b 100644
@@ -287,14 +287,16 @@ adfs_readdir(struct file *file, struct dir_context *ctx)
 int
 adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
 {
-	int ret = -EINVAL;
-#ifdef CONFIG_ADFS_FS_RW
 	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
 	struct adfs_dir dir;
+	int ret;
 
 	printk(KERN_INFO "adfs_dir_update: object %06x in dir %06x\n",
 		 obj->indaddr, obj->parent_id);
 
+	if (!IS_ENABLED(CONFIG_ADFS_FS_RW))
+		return -EINVAL;
+
 	if (!ops->update)
 		return -EINVAL;
 
@@ -328,7 +330,7 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
 		adfs_dir_forget(&dir);
 unlock:
 	up_write(&adfs_dir_rwsem);
-#endif
+
 	return ret;
 }
 
Signature · 1 line
2.20.1
#28 lore raw

PATCH 23/41 fs/adfs: dir: switch to iterate_shared method

In reply to Russell King - ARM Linux admin

There is nothing in our readdir (aka iterate) method that relies on the directory inode being exclusively locked, so switch to using the iterate_shared() hook rather than iterate().

---
 fs/adfs/dir.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
fs/adfs/dir.c +2 -3
index 931eefb2375b..2a8f5f1fd3d0 100644
@@ -235,8 +235,7 @@ void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
 	}
 }
 
-static int
-adfs_readdir(struct file *file, struct dir_context *ctx)
+static int adfs_iterate(struct file *file, struct dir_context *ctx)
 {
 	struct inode *inode = file_inode(file);
 	struct super_block *sb = inode->i_sb;
@@ -399,7 +398,7 @@ static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
 const struct file_operations adfs_dir_operations = {
 	.read		= generic_read_dir,
 	.llseek		= generic_file_llseek,
-	.iterate	= adfs_readdir,
+	.iterate_shared	= adfs_iterate,
 	.fsync		= generic_file_fsync,
 };
 
Signature · 1 line
2.20.1
#29 lore raw

PATCH 24/41 fs/adfs: dir: add more efficient iterate() per-format method

In reply to Russell King - ARM Linux admin

Rather than using setpos + getnext to iterate through the directory entries, pass iterate() down to the dir format code to populate the dirents.

---
 fs/adfs/adfs.h      |  1 +
 fs/adfs/dir.c       | 16 ++--------------
 fs/adfs/dir_f.c     | 18 ++++++++++++++++++
 fs/adfs/dir_fplus.c | 21 +++++++++++++++++++++
 4 files changed, 42 insertions(+), 14 deletions(-)
fs/adfs/adfs.h +1 -0
index 01d065937c01..cbf33f375e0b 100644
@@ -120,6 +120,7 @@ struct object_info {
 struct adfs_dir_ops {
 	int	(*read)(struct super_block *sb, unsigned int indaddr,
 			unsigned int size, struct adfs_dir *dir);
+	int	(*iterate)(struct adfs_dir *dir, struct dir_context *ctx);
 	int	(*setpos)(struct adfs_dir *dir, unsigned int fpos);
 	int	(*getnext)(struct adfs_dir *dir, struct object_info *obj);
 	int	(*update)(struct adfs_dir *dir, struct object_info *obj);
fs/adfs/dir.c +2 -14
index 2a8f5f1fd3d0..7fda44464121 100644
@@ -240,12 +240,8 @@ static int adfs_iterate(struct file *file, struct dir_context *ctx)
 	struct inode *inode = file_inode(file);
 	struct super_block *sb = inode->i_sb;
 	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
-	struct object_info obj;
 	struct adfs_dir dir;
-	int ret = 0;
-
-	if (ctx->pos >> 32)
-		return 0;
+	int ret;
 
 	down_read(&adfs_dir_rwsem);
 	ret = adfs_dir_read_inode(sb, inode, &dir);
@@ -263,15 +259,7 @@ static int adfs_iterate(struct file *file, struct dir_context *ctx)
 		ctx->pos = 2;
 	}
 
-	ret = ops->setpos(&dir, ctx->pos - 2);
-	if (ret)
-		goto unlock_relse;
-	while (ops->getnext(&dir, &obj) == 0) {
-		if (!dir_emit(ctx, obj.name, obj.name_len,
-			      obj.indaddr, DT_UNKNOWN))
-			break;
-		ctx->pos++;
-	}
+	ret = ops->iterate(&dir, ctx);
 
 unlock_relse:
 	up_read(&adfs_dir_rwsem);
fs/adfs/dir_f.c +18 -0
index 682df46d8d33..2e342871d6df 100644
@@ -302,6 +302,23 @@ adfs_f_getnext(struct adfs_dir *dir, struct object_info *obj)
 	return ret;
 }
 
+static int adfs_f_iterate(struct adfs_dir *dir, struct dir_context *ctx)
+{
+	struct object_info obj;
+	int pos = 5 + (ctx->pos - 2) * 26;
+
+	while (ctx->pos < 2 + ADFS_NUM_DIR_ENTRIES) {
+		if (__adfs_dir_get(dir, pos, &obj))
+			break;
+		if (!dir_emit(ctx, obj.name, obj.name_len,
+			      obj.indaddr, DT_UNKNOWN))
+			break;
+		pos += 26;
+		ctx->pos++;
+	}
+	return 0;
+}
+
 static int
 adfs_f_update(struct adfs_dir *dir, struct object_info *obj)
 {
@@ -359,6 +376,7 @@ adfs_f_update(struct adfs_dir *dir, struct object_info *obj)
 
 const struct adfs_dir_ops adfs_f_dir_ops = {
 	.read		= adfs_f_read,
+	.iterate	= adfs_f_iterate,
 	.setpos		= adfs_f_setpos,
 	.getnext	= adfs_f_getnext,
 	.update		= adfs_f_update,
fs/adfs/dir_fplus.c +21 -0
index ae11236515d0..edcbaa94ecb9 100644
@@ -118,8 +118,29 @@ adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
 	return 0;
 }
 
+static int adfs_fplus_iterate(struct adfs_dir *dir, struct dir_context *ctx)
+{
+	struct object_info obj;
+
+	if ((ctx->pos - 2) >> 32)
+		return 0;
+
+	if (adfs_fplus_setpos(dir, ctx->pos - 2))
+		return 0;
+
+	while (!adfs_fplus_getnext(dir, &obj)) {
+		if (!dir_emit(ctx, obj.name, obj.name_len,
+			      obj.indaddr, DT_UNKNOWN))
+			break;
+		ctx->pos++;
+	}
+
+	return 0;
+}
+
 const struct adfs_dir_ops adfs_fplus_dir_ops = {
 	.read		= adfs_fplus_read,
+	.iterate	= adfs_fplus_iterate,
 	.setpos		= adfs_fplus_setpos,
 	.getnext	= adfs_fplus_getnext,
 };
Signature · 1 line
2.20.1
#30 lore raw

PATCH 25/41 fs/adfs: dir: use pointers to access directory head/tails

In reply to Russell King - ARM Linux admin

Add and use pointers in the adfs_dir structure to access the directory head and tail structures, which will always be contiguous in a buffer. This allows us to avoid memcpy()ing the data in the new directory code, making it slightly more efficient.

---
 fs/adfs/adfs.h      | 12 ++++++++----
 fs/adfs/dir_f.c     | 42 +++++++++++++++++-------------------------
 fs/adfs/dir_fplus.c | 11 ++++-------
 3 files changed, 29 insertions(+), 36 deletions(-)
fs/adfs/adfs.h +8 -4
index cbf33f375e0b..1f431a42e14c 100644
@@ -26,8 +26,6 @@ static inline u16 adfs_filetype(u32 loadaddr)
 #define ADFS_NDA_PUBLIC_READ	(1 << 5)
 #define ADFS_NDA_PUBLIC_WRITE	(1 << 6)
 
-#include "dir_f.h"
-
 /*
  * adfs file system inode data in memory
  */
@@ -98,8 +96,14 @@ struct adfs_dir {
 	unsigned int		pos;
 	__u32			parent_id;
 
-	struct adfs_dirheader	dirhead;
-	union  adfs_dirtail	dirtail;
+	union {
+		struct adfs_dirheader	*dirhead;
+		struct adfs_bigdirheader *bighead;
+	};
+	union {
+		struct adfs_newdirtail	*newtail;
+		struct adfs_bigdirtail	*bigtail;
+	};
 };
 
 /*
fs/adfs/dir_f.c +17 -25

89 lines not on this page · raw message

fs/adfs/dir_fplus.c +4 -7
index edcbaa94ecb9..6f2dbcf6819b 100644
@@ -20,7 +20,7 @@ static int adfs_fplus_read(struct super_block *sb, u32 indaddr,
 	if (ret)
 		return ret;
 
-	h = (struct adfs_bigdirheader *)dir->bhs[0]->b_data;
+	dir->bighead = h = (void *)dir->bhs[0]->b_data;
 	dirsize = le32_to_cpu(h->bigdirsize);
 	if (dirsize != size) {
 		adfs_msg(sb, KERN_WARNING,
@@ -40,7 +40,7 @@ static int adfs_fplus_read(struct super_block *sb, u32 indaddr,
 	if (ret)
 		return ret;
 
-	t = (struct adfs_bigdirtail *)
+	dir->bigtail = t = (struct adfs_bigdirtail *)
 		(dir->bhs[dir->nr_buffers - 1]->b_data + (sb->s_blocksize - 8));
 
 	if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) ||
@@ -62,11 +62,9 @@ static int adfs_fplus_read(struct super_block *sb, u32 indaddr,
 static int
 adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos)
 {
-	struct adfs_bigdirheader *h =
-		(struct adfs_bigdirheader *) dir->bhs[0]->b_data;
 	int ret = -ENOENT;
 
-	if (fpos <= le32_to_cpu(h->bigdirentries)) {
+	if (fpos <= le32_to_cpu(dir->bighead->bigdirentries)) {
 		dir->pos = fpos;
 		ret = 0;
 	}
@@ -77,8 +75,7 @@ adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos)
 static int
 adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
 {
-	struct adfs_bigdirheader *h =
-		(struct adfs_bigdirheader *) dir->bhs[0]->b_data;
+	struct adfs_bigdirheader *h = dir->bighead;
 	struct adfs_bigdirentry bde;
 	unsigned int offset;
 	int ret;
Signature · 1 line
2.20.1
#31 lore raw

PATCH 26/41 fs/adfs: newdir: factor out directory format validation

In reply to Russell King - ARM Linux admin

We have two locations where we validate the new directory format, so factor this out to a helper.

---
 fs/adfs/dir_f.c | 48 ++++++++++++++++++++----------------------------
 1 file changed, 20 insertions(+), 28 deletions(-)
fs/adfs/dir_f.c +20 -28
index 7e56fcc21303..196706d581bf 100644
@@ -121,6 +121,21 @@ adfs_dir_checkbyte(const struct adfs_dir *dir)
 	return (dircheck ^ (dircheck >> 8) ^ (dircheck >> 16) ^ (dircheck >> 24)) & 0xff;
 }
 
+static int adfs_f_validate(struct adfs_dir *dir)
+{
+	struct adfs_dirheader *head = dir->dirhead;
+	struct adfs_newdirtail *tail = dir->newtail;
+
+	if (head->startmasseq != tail->endmasseq ||
+	    (memcmp(&head->startname, "Nick", 4) &&
+	     memcmp(&head->startname, "Hugo", 4)) ||
+	    memcmp(&head->startname, &tail->endname, 4) ||
+	    adfs_dir_checkbyte(dir) != tail->dircheckbyte)
+		return -EIO;
+
+	return 0;
+}
+
 /* Read and check that a directory is valid */
 static int adfs_dir_read(struct super_block *sb, u32 indaddr,
 			 unsigned int size, struct adfs_dir *dir)
@@ -142,15 +157,7 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr,
 	dir->dirhead = bufoff(dir->bh, 0);
 	dir->newtail = bufoff(dir->bh, 2007);
 
-	if (dir->dirhead->startmasseq != dir->newtail->endmasseq ||
-	    memcmp(&dir->dirhead->startname, &dir->newtail->endname, 4))
-		goto bad_dir;
-
-	if (memcmp(&dir->dirhead->startname, "Nick", 4) &&
-	    memcmp(&dir->dirhead->startname, "Hugo", 4))
-		goto bad_dir;
-
-	if (adfs_dir_checkbyte(dir) != dir->newtail->dircheckbyte)
+	if (adfs_f_validate(dir))
 		goto bad_dir;
 
 	return 0;
@@ -327,7 +334,7 @@ adfs_f_update(struct adfs_dir *dir, struct object_info *obj)
 	ret = adfs_dir_find_entry(dir, obj->indaddr);
 	if (ret < 0) {
 		adfs_error(dir->sb, "unable to locate entry to update");
-		goto out;
+		return ret;
 	}
 
 	__adfs_dir_put(dir, ret, obj);
@@ -344,26 +351,11 @@ adfs_f_update(struct adfs_dir *dir, struct object_info *obj)
 	 */
 	dir->newtail->dircheckbyte = ret;
 
-#if 1
-	if (dir->dirhead->startmasseq != dir->newtail->endmasseq ||
-	    memcmp(&dir->dirhead->startname, &dir->newtail->endname, 4))
-		goto bad_dir;
-
-	if (memcmp(&dir->dirhead->startname, "Nick", 4) &&
-	    memcmp(&dir->dirhead->startname, "Hugo", 4))
-		goto bad_dir;
+	ret = adfs_f_validate(dir);
+	if (ret)
+		adfs_error(dir->sb, "whoops!  I broke a directory!");
 
-	if (adfs_dir_checkbyte(dir) != dir->newtail->dircheckbyte)
-		goto bad_dir;
-#endif
-	ret = 0;
-out:
 	return ret;
-#if 1
-bad_dir:
-	adfs_error(dir->sb, "whoops!  I broke a directory!");
-	return -EIO;
-#endif
 }
 
 const struct adfs_dir_ops adfs_f_dir_ops = {
Signature · 1 line
2.20.1
#32 lore raw

PATCH 27/41 fs/adfs: newdir: improve directory validation

In reply to Russell King - ARM Linux admin

Check that the lastmask and reserved fields are all zero, as per the documentation.

---
 fs/adfs/dir_f.c | 1 +
 1 file changed, 1 insertion(+)
fs/adfs/dir_f.c +1 -0
index 196706d581bf..ebe8616ee533 100644
@@ -127,6 +127,7 @@ static int adfs_f_validate(struct adfs_dir *dir)
 	struct adfs_newdirtail *tail = dir->newtail;
 
 	if (head->startmasseq != tail->endmasseq ||
+	    tail->dirlastmask || tail->reserved[0] || tail->reserved[1] ||
 	    (memcmp(&head->startname, "Nick", 4) &&
 	     memcmp(&head->startname, "Hugo", 4)) ||
 	    memcmp(&head->startname, &tail->endname, 4) ||
Signature · 1 line
2.20.1
#33 lore raw

PATCH 28/41 fs/adfs: newdir: merge adfs_dir_read() into adfs_f_read()

In reply to Russell King - ARM Linux admin

adfs_dir_read() is only called from adfs_f_read(), so merge it into that function. As new directories are always 2048 bytes in size, (which we rely on elsewhere) we can consolidate some of the code.

---
 fs/adfs/dir_f.c | 33 +++++++--------------------------
 1 file changed, 7 insertions(+), 26 deletions(-)
fs/adfs/dir_f.c +7 -26
index ebe8616ee533..dbb4f1ef7bb7 100644
@@ -138,20 +138,16 @@ static int adfs_f_validate(struct adfs_dir *dir)
 }
 
 /* Read and check that a directory is valid */
-static int adfs_dir_read(struct super_block *sb, u32 indaddr,
-			 unsigned int size, struct adfs_dir *dir)
+static int adfs_f_read(struct super_block *sb, u32 indaddr, unsigned int size,
+		       struct adfs_dir *dir)
 {
 	const unsigned int blocksize_bits = sb->s_blocksize_bits;
 	int ret;
 
-	/*
-	 * Directories which are not a multiple of 2048 bytes
-	 * are considered bad v2 [3.6]
-	 */
-	if (size & 2047)
-		goto bad_dir;
+	if (size && size != ADFS_NEWDIR_SIZE)
+		return -EIO;
 
-	ret = adfs_dir_read_buffers(sb, indaddr, size, dir);
+	ret = adfs_dir_read_buffers(sb, indaddr, ADFS_NEWDIR_SIZE, dir);
 	if (ret)
 		return ret;
 
@@ -161,6 +157,8 @@ static int adfs_dir_read(struct super_block *sb, u32 indaddr,
 	if (adfs_f_validate(dir))
 		goto bad_dir;
 
+	dir->parent_id = adfs_readval(dir->newtail->dirparent, 3);
+
 	return 0;
 
 bad_dir:
@@ -271,23 +269,6 @@ static int adfs_dir_find_entry(struct adfs_dir *dir, u32 indaddr)
 	return ret;
 }
 
-static int adfs_f_read(struct super_block *sb, u32 indaddr, unsigned int size,
-		       struct adfs_dir *dir)
-{
-	int ret;
-
-	if (size != ADFS_NEWDIR_SIZE)
-		return -EIO;
-
-	ret = adfs_dir_read(sb, indaddr, size, dir);
-	if (ret)
-		adfs_error(sb, "unable to read directory");
-	else
-		dir->parent_id = adfs_readval(dir->newtail->dirparent, 3);
-
-	return ret;
-}
-
 static int
 adfs_f_setpos(struct adfs_dir *dir, unsigned int fpos)
 {
Signature · 1 line
2.20.1
#34 lore raw

PATCH 29/41 fs/adfs: newdir: clean up adfs_f_update()

In reply to Russell King - ARM Linux admin

__adfs_dir_put() and adfs_dir_find_entry() are only called from adfs_f_update(), so move them into this function, removing some unnecessary entry copying by doing so.

---
 fs/adfs/dir_f.c | 73 ++++++++++++++++---------------------------------
 1 file changed, 24 insertions(+), 49 deletions(-)
fs/adfs/dir_f.c +24 -49
index dbb4f1ef7bb7..36cfadb2b893 100644
@@ -229,46 +229,6 @@ __adfs_dir_get(struct adfs_dir *dir, int pos, struct object_info *obj)
 	return 0;
 }
 
-static int
-__adfs_dir_put(struct adfs_dir *dir, int pos, struct object_info *obj)
-{
-	struct adfs_direntry de;
-	int ret;
-
-	ret = adfs_dir_copyfrom(&de, dir, pos, 26);
-	if (ret)
-		return ret;
-
-	adfs_obj2dir(&de, obj);
-
-	return adfs_dir_copyto(dir, pos, &de, 26);
-}
-
-/*
- * the caller is responsible for holding the necessary
- * locks.
- */
-static int adfs_dir_find_entry(struct adfs_dir *dir, u32 indaddr)
-{
-	int pos, ret;
-
-	ret = -ENOENT;
-
-	for (pos = 5; pos < ADFS_NUM_DIR_ENTRIES * 26 + 5; pos += 26) {
-		struct object_info obj;
-
-		if (!__adfs_dir_get(dir, pos, &obj))
-			break;
-
-		if (obj.indaddr == indaddr) {
-			ret = pos;
-			break;
-		}
-	}
-
-	return ret;
-}
-
 static int
 adfs_f_setpos(struct adfs_dir *dir, unsigned int fpos)
 {
@@ -308,18 +268,33 @@ static int adfs_f_iterate(struct adfs_dir *dir, struct dir_context *ctx)
 	return 0;
 }
 
-static int
-adfs_f_update(struct adfs_dir *dir, struct object_info *obj)
+static int adfs_f_update(struct adfs_dir *dir, struct object_info *obj)
 {
-	int ret;
+	struct adfs_direntry de;
+	int offset, ret;
 
-	ret = adfs_dir_find_entry(dir, obj->indaddr);
-	if (ret < 0) {
-		adfs_error(dir->sb, "unable to locate entry to update");
-		return ret;
-	}
+	offset = 5 - (int)sizeof(de);
+
+	do {
+		offset += sizeof(de);
+		ret = adfs_dir_copyfrom(&de, dir, offset, sizeof(de));
+		if (ret) {
+			adfs_error(dir->sb, "error reading directory entry");
+			return -ENOENT;
+		}
+		if (!de.dirobname[0]) {
+			adfs_error(dir->sb, "unable to locate entry to update");
+			return -ENOENT;
+		}
+	} while (adfs_readval(de.dirinddiscadd, 3) != obj->indaddr);
+
+	/* Update the directory entry with the new object state */
+	adfs_obj2dir(&de, obj);
 
-	__adfs_dir_put(dir, ret, obj);
+	/* Write the directory entry back to the directory */
+	ret = adfs_dir_copyto(dir, pos, &de, 26);
+	if (ret)
+		return ret;
  
 	/*
 	 * Increment directory sequence number
Signature · 1 line
2.20.1
#35 lore raw

PATCH 30/41 fs/adfs: newdir: split out directory commit from update

In reply to Russell King - ARM Linux admin

After changing a directory, we need to update the sequence numbers and calculate the new check byte before the directory is scheduled to be written back to the media. Since this needs to happen for any change to the directory, move this into a separate method.

---
 fs/adfs/adfs.h  |  1 +
 fs/adfs/dir.c   |  4 ++++
 fs/adfs/dir_f.c | 26 +++++++++++++-------------
 3 files changed, 18 insertions(+), 13 deletions(-)
fs/adfs/adfs.h +1 -0
index 1f431a42e14c..c05555252fec 100644
@@ -130,6 +130,7 @@ struct adfs_dir_ops {
 	int	(*update)(struct adfs_dir *dir, struct object_info *obj);
 	int	(*create)(struct adfs_dir *dir, struct object_info *obj);
 	int	(*remove)(struct adfs_dir *dir, struct object_info *obj);
+	int	(*commit)(struct adfs_dir *dir);
 };
 
 struct adfs_discmap {
fs/adfs/dir.c +4 -0
index 7fda44464121..3d4bbe836fb5 100644
@@ -293,6 +293,10 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
 		goto unlock;
 
 	ret = ops->update(&dir, obj);
+	if (ret)
+		goto forget;
+
+	ret = ops->commit(&dir);
 	if (ret)
 		goto forget;
 	up_write(&adfs_dir_rwsem);
fs/adfs/dir_f.c +13 -13
index 36cfadb2b893..30d526fecc3f 100644
@@ -292,25 +292,24 @@ static int adfs_f_update(struct adfs_dir *dir, struct object_info *obj)
 	adfs_obj2dir(&de, obj);
 
 	/* Write the directory entry back to the directory */
-	ret = adfs_dir_copyto(dir, pos, &de, 26);
-	if (ret)
-		return ret;
- 
-	/*
-	 * Increment directory sequence number
-	 */
+	return adfs_dir_copyto(dir, offset, &de, 26);
+}
+
+static int adfs_f_commit(struct adfs_dir *dir)
+{
+	int ret;
+
+	/* Increment directory sequence number */
 	dir->dirhead->startmasseq += 1;
 	dir->newtail->endmasseq += 1;
 
-	ret = adfs_dir_checkbyte(dir);
-	/*
-	 * Update directory check byte
-	 */
-	dir->newtail->dircheckbyte = ret;
+	/* Update directory check byte */
+	dir->newtail->dircheckbyte = adfs_dir_checkbyte(dir);
 
+	/* Make sure the directory still validates correctly */
 	ret = adfs_f_validate(dir);
 	if (ret)
-		adfs_error(dir->sb, "whoops!  I broke a directory!");
+		adfs_msg(dir->sb, KERN_ERR, "error: update broke directory");
 
 	return ret;
 }
@@ -321,4 +320,5 @@ const struct adfs_dir_ops adfs_f_dir_ops = {
 	.setpos		= adfs_f_setpos,
 	.getnext	= adfs_f_getnext,
 	.update		= adfs_f_update,
+	.commit		= adfs_f_commit,
 };
Signature · 1 line
2.20.1
#36 lore raw

PATCH 31/41 fs/adfs: bigdir: factor out directory entry offset calculation

In reply to Russell King - ARM Linux admin

Factor out the directory entry byte offset calculation.

---
 fs/adfs/dir_fplus.c | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)
fs/adfs/dir_fplus.c +11 -6
index 6f2dbcf6819b..393921f5121e 100644
@@ -7,6 +7,15 @@
 #include "adfs.h"
 #include "dir_fplus.h"
 
+/* Return the byte offset to directory entry pos */
+static unsigned int adfs_fplus_offset(const struct adfs_bigdirheader *h,
+				      unsigned int pos)
+{
+	return offsetof(struct adfs_bigdirheader, bigdirname) +
+	       ALIGN(le32_to_cpu(h->bigdirnamelen), 4) +
+	       pos * sizeof(struct adfs_bigdirentry);
+}
+
 static int adfs_fplus_read(struct super_block *sb, u32 indaddr,
 			   unsigned int size, struct adfs_dir *dir)
 {
@@ -83,9 +92,7 @@ adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
 	if (dir->pos >= le32_to_cpu(h->bigdirentries))
 		return -ENOENT;
 
-	offset = offsetof(struct adfs_bigdirheader, bigdirname);
-	offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3);
-	offset += dir->pos * sizeof(struct adfs_bigdirentry);
+	offset = adfs_fplus_offset(h, dir->pos);
 
 	ret = adfs_dir_copyfrom(&bde, dir, offset,
 				sizeof(struct adfs_bigdirentry));
@@ -99,9 +106,7 @@ adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
 	obj->attr     = le32_to_cpu(bde.bigdirattr);
 	obj->name_len = le32_to_cpu(bde.bigdirobnamelen);
 
-	offset = offsetof(struct adfs_bigdirheader, bigdirname);
-	offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3);
-	offset += le32_to_cpu(h->bigdirentries) * sizeof(struct adfs_bigdirentry);
+	offset = adfs_fplus_offset(h, le32_to_cpu(h->bigdirentries));
 	offset += le32_to_cpu(bde.bigdirobnameptr);
 
 	ret = adfs_dir_copyfrom(obj->name, dir, offset, obj->name_len);
Signature · 1 line
2.20.1
#37 lore raw

PATCH 32/41 fs/adfs: bigdir: extract directory validation

In reply to Russell King - ARM Linux admin

Extract the directory validation from the directory reading function as we will want to re-use this code.

---
 fs/adfs/dir_fplus.c | 41 +++++++++++++++++++++++++++++++----------
 1 file changed, 31 insertions(+), 10 deletions(-)
fs/adfs/dir_fplus.c +31 -10
index 393921f5121e..b83a74e9ff6d 100644
@@ -16,6 +16,30 @@ static unsigned int adfs_fplus_offset(const struct adfs_bigdirheader *h,
 	       pos * sizeof(struct adfs_bigdirentry);
 }
 
+static int adfs_fplus_validate_header(const struct adfs_bigdirheader *h)
+{
+	unsigned int size = le32_to_cpu(h->bigdirsize);
+
+	if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 ||
+	    h->bigdirversion[2] != 0 ||
+	    h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME) ||
+	    size & 2047)
+		return -EIO;
+
+	return 0;
+}
+
+static int adfs_fplus_validate_tail(const struct adfs_bigdirheader *h,
+				    const struct adfs_bigdirtail *t)
+{
+	if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) ||
+	    t->bigdirendmasseq != h->startmasseq ||
+	    t->reserved[0] != 0 || t->reserved[1] != 0)
+		return -EIO;
+
+	return 0;
+}
+
 static int adfs_fplus_read(struct super_block *sb, u32 indaddr,
 			   unsigned int size, struct adfs_dir *dir)
 {
@@ -30,6 +54,11 @@ static int adfs_fplus_read(struct super_block *sb, u32 indaddr,
 		return ret;
 
 	dir->bighead = h = (void *)dir->bhs[0]->b_data;
+	if (adfs_fplus_validate_header(h)) {
+		adfs_error(sb, "dir %06x has malformed header", indaddr);
+		goto out;
+	}
+
 	dirsize = le32_to_cpu(h->bigdirsize);
 	if (dirsize != size) {
 		adfs_msg(sb, KERN_WARNING,
@@ -37,13 +66,6 @@ static int adfs_fplus_read(struct super_block *sb, u32 indaddr,
 			 indaddr, dirsize, size);
 	}
 
-	if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 ||
-	    h->bigdirversion[2] != 0 || size & 2047 ||
-	    h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME)) {
-		adfs_error(sb, "dir %06x has malformed header", indaddr);
-		goto out;
-	}
-
 	/* Read remaining buffers */
 	ret = adfs_dir_read_buffers(sb, indaddr, dirsize, dir);
 	if (ret)
@@ -52,9 +74,8 @@ static int adfs_fplus_read(struct super_block *sb, u32 indaddr,
 	dir->bigtail = t = (struct adfs_bigdirtail *)
 		(dir->bhs[dir->nr_buffers - 1]->b_data + (sb->s_blocksize - 8));
 
-	if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) ||
-	    t->bigdirendmasseq != h->startmasseq ||
-	    t->reserved[0] != 0 || t->reserved[1] != 0) {
+	ret = adfs_fplus_validate_tail(h, t);
+	if (ret) {
 		adfs_error(sb, "dir %06x has malformed tail", indaddr);
 		goto out;
 	}
Signature · 1 line
2.20.1
#38 lore raw

PATCH 33/41 fs/adfs: bigdir: directory validation strengthening

In reply to Russell King - ARM Linux admin

Strengthen the directory validation by ensuring that the header fields contain sensible values that fit inside the directory, and limit the directory size to 4MB as per RISC OS requirements.

---
 fs/adfs/dir_fplus.c | 29 ++++++++++++++++++++++++++++-
 1 file changed, 28 insertions(+), 1 deletion(-)
fs/adfs/dir_fplus.c +28 -1
index b83a74e9ff6d..a2fa416fbb6d 100644
@@ -19,11 +19,38 @@ static unsigned int adfs_fplus_offset(const struct adfs_bigdirheader *h,
 static int adfs_fplus_validate_header(const struct adfs_bigdirheader *h)
 {
 	unsigned int size = le32_to_cpu(h->bigdirsize);
+	unsigned int len;
 
 	if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 ||
 	    h->bigdirversion[2] != 0 ||
 	    h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME) ||
-	    size & 2047)
+	    !size || size & 2047 || size > SZ_4M)
+		return -EIO;
+
+	size -= sizeof(struct adfs_bigdirtail) +
+		offsetof(struct adfs_bigdirheader, bigdirname);
+
+	/* Check that bigdirnamelen fits within the directory */
+	len = ALIGN(le32_to_cpu(h->bigdirnamelen), 4);
+	if (len > size)
+		return -EIO;
+
+	size -= len;
+
+	/* Check that bigdirnamesize fits within the directory */
+	len = le32_to_cpu(h->bigdirnamesize);
+	if (len > size)
+		return -EIO;
+
+	size -= len;
+
+	/*
+	 * Avoid division, we know that absolute maximum number of entries
+	 * can not be so large to cause overflow of the multiplication below.
+	 */
+	len = le32_to_cpu(h->bigdirentries);
+	if (len > SZ_4M / sizeof(struct adfs_bigdirentry) ||
+	    len * sizeof(struct adfs_bigdirentry) > size)
 		return -EIO;
 
 	return 0;
Signature · 1 line
2.20.1
#39 lore raw

PATCH 34/41 fs/adfs: bigdir: calculate and validate directory checkbyte

In reply to Russell King - ARM Linux admin

When reading a big directory, calculate the validate the directory checkbyte to ensure that the directory contents are valid.

---
 fs/adfs/dir_fplus.c | 38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)
fs/adfs/dir_fplus.c +38 -0
index a2fa416fbb6d..4ab8987962f0 100644
@@ -67,6 +67,39 @@ static int adfs_fplus_validate_tail(const struct adfs_bigdirheader *h,
 	return 0;
 }
 
+static u8 adfs_fplus_checkbyte(struct adfs_dir *dir)
+{
+	struct adfs_bigdirheader *h = dir->bighead;
+	struct adfs_bigdirtail *t = dir->bigtail;
+	unsigned int end, bs, bi, i;
+	__le32 *bp;
+	u32 dircheck;
+
+	end = adfs_fplus_offset(h, le32_to_cpu(h->bigdirentries)) +
+		le32_to_cpu(h->bigdirnamesize);
+
+	/* Accumulate the contents of the header, entries and names */
+	for (dircheck = 0, bi = 0; end; bi++) {
+		bp = (void *)dir->bhs[bi]->b_data;
+		bs = dir->bhs[bi]->b_size;
+		if (bs > end)
+			bs = end;
+
+		for (i = 0; i < bs; i += sizeof(u32))
+			dircheck = ror32(dircheck, 13) ^ le32_to_cpup(bp++);
+
+		end -= bs;
+	}
+
+	/* Accumulate the contents of the tail except for the check byte */
+	dircheck = ror32(dircheck, 13) ^ le32_to_cpu(t->bigdirendname);
+	dircheck = ror32(dircheck, 13) ^ t->bigdirendmasseq;
+	dircheck = ror32(dircheck, 13) ^ t->reserved[0];
+	dircheck = ror32(dircheck, 13) ^ t->reserved[1];
+
+	return dircheck ^ dircheck >> 8 ^ dircheck >> 16 ^ dircheck >> 24;
+}
+
 static int adfs_fplus_read(struct super_block *sb, u32 indaddr,
 			   unsigned int size, struct adfs_dir *dir)
 {
@@ -107,6 +140,11 @@ static int adfs_fplus_read(struct super_block *sb, u32 indaddr,
 		goto out;
 	}
 
+	if (adfs_fplus_checkbyte(dir) != t->bigdircheckbyte) {
+		adfs_error(sb, "dir %06x checkbyte mismatch\n", indaddr);
+		goto out;
+	}
+
 	dir->parent_id = le32_to_cpu(h->bigdirparent);
 	return 0;
 
Signature · 1 line
2.20.1
#40 lore raw

PATCH 35/41 fs/adfs: bigdir: implement directory update support

In reply to Russell King - ARM Linux admin

Implement big directory entry update support in the same way that we do for new directories.

---
 fs/adfs/dir_fplus.c | 54 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 53 insertions(+), 1 deletion(-)
fs/adfs/dir_fplus.c +53 -1
index 4ab8987962f0..48ea299b6ece 100644
@@ -120,7 +120,7 @@ static int adfs_fplus_read(struct super_block *sb, u32 indaddr,
 	}
 
 	dirsize = le32_to_cpu(h->bigdirsize);
-	if (dirsize != size) {
+	if (size && dirsize != size) {
 		adfs_msg(sb, KERN_WARNING,
 			 "dir %06x header size %X does not match directory size %X",
 			 indaddr, dirsize, size);
@@ -226,9 +226,61 @@ static int adfs_fplus_iterate(struct adfs_dir *dir, struct dir_context *ctx)
 	return 0;
 }
 
+static int adfs_fplus_update(struct adfs_dir *dir, struct object_info *obj)
+{
+	struct adfs_bigdirheader *h = dir->bighead;
+	struct adfs_bigdirentry bde;
+	int offset, end, ret;
+
+	offset = adfs_fplus_offset(h, 0) - sizeof(bde);
+	end = adfs_fplus_offset(h, le32_to_cpu(h->bigdirentries));
+
+	do {
+		offset += sizeof(bde);
+		if (offset >= end) {
+			adfs_error(dir->sb, "unable to locate entry to update");
+			return -ENOENT;
+		}
+		ret = adfs_dir_copyfrom(&bde, dir, offset, sizeof(bde));
+		if (ret) {
+			adfs_error(dir->sb, "error reading directory entry");
+			return -ENOENT;
+		}
+	} while (le32_to_cpu(bde.bigdirindaddr) != obj->indaddr);
+
+	bde.bigdirload    = cpu_to_le32(obj->loadaddr);
+	bde.bigdirexec    = cpu_to_le32(obj->execaddr);
+	bde.bigdirlen     = cpu_to_le32(obj->size);
+	bde.bigdirindaddr = cpu_to_le32(obj->indaddr);
+	bde.bigdirattr    = cpu_to_le32(obj->attr);
+
+	return adfs_dir_copyto(dir, offset, &bde, sizeof(bde));
+}
+
+static int adfs_fplus_commit(struct adfs_dir *dir)
+{
+	int ret;
+
+	/* Increment directory sequence number */
+	dir->bighead->startmasseq += 1;
+	dir->bigtail->bigdirendmasseq += 1;
+
+	/* Update directory check byte */
+	dir->bigtail->bigdircheckbyte = adfs_fplus_checkbyte(dir);
+
+	/* Make sure the directory still validates correctly */
+	ret = adfs_fplus_validate_header(dir->bighead);
+	if (ret == 0)
+		ret = adfs_fplus_validate_tail(dir->bighead, dir->bigtail);
+
+	return ret;
+}
+
 const struct adfs_dir_ops adfs_fplus_dir_ops = {
 	.read		= adfs_fplus_read,
 	.iterate	= adfs_fplus_iterate,
 	.setpos		= adfs_fplus_setpos,
 	.getnext	= adfs_fplus_getnext,
+	.update		= adfs_fplus_update,
+	.commit		= adfs_fplus_commit,
 };
Signature · 1 line
2.20.1
#41 lore raw

PATCH 36/41 fs/adfs: super: fix inode dropping

In reply to Russell King - ARM Linux admin

When we have write support enabled, we must not drop inodes before they have been written back, otherwise we lose updates to the filesystem on umount. Keep the inodes around unless we are built in read-only mode, or we are mounted read-only.

---
 fs/adfs/super.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
fs/adfs/super.c +7 -1
index b2455e9ab923..9c93122925cf 100644
@@ -231,6 +231,12 @@ static void adfs_free_inode(struct inode *inode)
 	kmem_cache_free(adfs_inode_cachep, ADFS_I(inode));
 }
 
+static int adfs_drop_inode(struct inode *inode)
+{
+	/* always drop inodes if we are read-only */
+	return !IS_ENABLED(CONFIG_ADFS_FS_RW) || IS_RDONLY(inode);
+}
+
 static void init_once(void *foo)
 {
 	struct adfs_inode_info *ei = (struct adfs_inode_info *) foo;
@@ -263,7 +269,7 @@ static void destroy_inodecache(void)
 static const struct super_operations adfs_sops = {
 	.alloc_inode	= adfs_alloc_inode,
 	.free_inode	= adfs_free_inode,
-	.drop_inode	= generic_delete_inode,
+	.drop_inode	= adfs_drop_inode,
 	.write_inode	= adfs_write_inode,
 	.put_super	= adfs_put_super,
 	.statfs		= adfs_statfs,
Signature · 1 line
2.20.1
#42 lore raw

PATCH 37/41 fs/adfs: dir: remove debug in adfs_dir_update()

In reply to Russell King - ARM Linux admin

Remove the noisy debug in adfs_dir_update().

---
 fs/adfs/dir.c | 3 ---
 1 file changed, 3 deletions(-)
fs/adfs/dir.c +0 -3
index 3d4bbe836fb5..dd940f17767d 100644
@@ -278,9 +278,6 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
 	struct adfs_dir dir;
 	int ret;
 
-	printk(KERN_INFO "adfs_dir_update: object %06x in dir %06x\n",
-		 obj->indaddr, obj->parent_id);
-
 	if (!IS_ENABLED(CONFIG_ADFS_FS_RW))
 		return -EINVAL;
 
Signature · 1 line
2.20.1
#43 lore raw

PATCH 38/41 fs/adfs: super: extract filesystem block probe

In reply to Russell King - ARM Linux admin

Separate the filesystem block probing from the superblock filling so we can support other ADFS filesystem formats, such as the single-zone E and E+ floppy image formats which do not have a boot block.

---
 fs/adfs/super.c | 149 +++++++++++++++++++++++++-----------------------
 1 file changed, 78 insertions(+), 71 deletions(-)
fs/adfs/super.c +78 -71
index 9c93122925cf..4c06b2d5a861 100644
@@ -277,13 +277,80 @@ static const struct super_operations adfs_sops = {
 	.show_options	= adfs_show_options,
 };
 
-static int adfs_fill_super(struct super_block *sb, void *data, int silent)
+static int adfs_probe(struct super_block *sb, unsigned int offset, int silent,
+		      int (*validate)(struct super_block *sb,
+				      struct buffer_head *bh,
+				      struct adfs_discrecord **bhp))
 {
+	struct adfs_sb_info *asb = ADFS_SB(sb);
 	struct adfs_discrecord *dr;
 	struct buffer_head *bh;
-	struct object_info root_obj;
+	unsigned int blocksize = BLOCK_SIZE;
+	int ret, try;
+
+	for (try = 0; try < 2; try++) {
+		/* try to set the requested block size */
+		if (sb->s_blocksize != blocksize &&
+		    !sb_set_blocksize(sb, blocksize)) {
+			if (!silent)
+				adfs_msg(sb, KERN_ERR,
+					 "error: unsupported blocksize");
+			return -EINVAL;
+		}
+
+		/* read the buffer */
+		bh = sb_bread(sb, offset >> sb->s_blocksize_bits);
+		if (!bh) {
+			adfs_msg(sb, KERN_ERR,
+				 "error: unable to read block %u, try %d",
+				 offset >> sb->s_blocksize_bits, try);
+			return -EIO;
+		}
+
+		/* validate it */
+		ret = validate(sb, bh, &dr);
+		if (ret) {
+			brelse(bh);
+			return ret;
+		}
+
+		/* does the block size match the filesystem block size? */
+		blocksize = 1 << dr->log2secsize;
+		if (sb->s_blocksize == blocksize) {
+			asb->s_map = adfs_read_map(sb, dr);
+			brelse(bh);
+			return PTR_ERR_OR_ZERO(asb->s_map);
+		}
+
+		brelse(bh);
+	}
+
+	return -EIO;
+}
+
+static int adfs_validate_bblk(struct super_block *sb, struct buffer_head *bh,
+			      struct adfs_discrecord **drp)
+{
+	struct adfs_discrecord *dr;
 	unsigned char *b_data;
-	unsigned int blocksize;
+
+	b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize);
+	if (adfs_checkbblk(b_data))
+		return -EILSEQ;
+
+	/* Do some sanity checks on the ADFS disc record */
+	dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
+	if (adfs_checkdiscrecord(dr))
+		return -EILSEQ;
+
+	*drp = dr;
+	return 0;
+}
+
+static int adfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+	struct adfs_discrecord *dr;
+	struct object_info root_obj;
 	struct adfs_sb_info *asb;
 	struct inode *root;
 	int ret = -EINVAL;
@@ -308,72 +375,19 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent)
 	if (parse_options(sb, asb, data))
 		goto error;
 
-	sb_set_blocksize(sb, BLOCK_SIZE);
-	if (!(bh = sb_bread(sb, ADFS_DISCRECORD / BLOCK_SIZE))) {
-		adfs_msg(sb, KERN_ERR, "error: unable to read superblock");
-		ret = -EIO;
-		goto error;
-	}
-
-	b_data = bh->b_data + (ADFS_DISCRECORD % BLOCK_SIZE);
-
-	if (adfs_checkbblk(b_data)) {
-		ret = -EINVAL;
-		goto error_badfs;
-	}
-
-	dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
-
-	/*
-	 * Do some sanity checks on the ADFS disc record
-	 */
-	if (adfs_checkdiscrecord(dr)) {
-		ret = -EINVAL;
-		goto error_badfs;
-	}
-
-	blocksize = 1 << dr->log2secsize;
-	brelse(bh);
-
-	if (sb_set_blocksize(sb, blocksize)) {
-		bh = sb_bread(sb, ADFS_DISCRECORD / sb->s_blocksize);
-		if (!bh) {
-			adfs_msg(sb, KERN_ERR,
-				 "error: couldn't read superblock on 2nd try.");
-			ret = -EIO;
-			goto error;
-		}
-		b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize);
-		if (adfs_checkbblk(b_data)) {
-			adfs_msg(sb, KERN_ERR,
-				 "error: disc record mismatch, very weird!");
-			ret = -EINVAL;
-			goto error_free_bh;
-		}
-		dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
-	} else {
+	/* Try to probe the filesystem boot block */
+	ret = adfs_probe(sb, ADFS_DISCRECORD, silent, adfs_validate_bblk);
+	if (ret == -EILSEQ) {
 		if (!silent)
 			adfs_msg(sb, KERN_ERR,
-				 "error: unsupported blocksize");
+				 "error: can't find an ADFS filesystem on dev %s.",
+				 sb->s_id);
 		ret = -EINVAL;
-		goto error;
 	}
+	if (ret)
+		goto error;
 
-	/*
-	 * blocksize on this device should now be set to the ADFS log2secsize
-	 */
-
-	asb->s_map = adfs_read_map(sb, dr);
-	if (IS_ERR(asb->s_map)) {
-		ret =  PTR_ERR(asb->s_map);
-		goto error_free_bh;
-	}
-
-	brelse(bh);
-
-	/*
-	 * set up enough so that we can read an inode
-	 */
+	/* set up enough so that we can read an inode */
 	sb->s_op = &adfs_sops;
 
 	dr = adfs_map_discrecord(asb->s_map);
@@ -417,13 +431,6 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent)
 	}
 	return 0;
 
-error_badfs:
-	if (!silent)
-		adfs_msg(sb, KERN_ERR,
-			 "error: can't find an ADFS filesystem on dev %s.",
-			 sb->s_id);
-error_free_bh:
-	brelse(bh);
 error:
 	sb->s_fs_info = NULL;
 	kfree(asb);
Signature · 1 line
2.20.1
#44 lore raw

PATCH 39/41 fs/adfs: super: add support for E and E+ floppy image formats

In reply to Russell King - ARM Linux admin

Add support for ADFS E and E+ floppy image formats, which, unlike their hard disk variants, do not have a filesystem boot block - they have a single map zone, with the map fragment stored at sector 0.

---
 fs/adfs/super.c | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)
fs/adfs/super.c +17 -1
index 4c06b2d5a861..a3cc8ecb50da 100644
@@ -347,6 +347,20 @@ static int adfs_validate_bblk(struct super_block *sb, struct buffer_head *bh,
 	return 0;
 }
 
+static int adfs_validate_dr0(struct super_block *sb, struct buffer_head *bh,
+			      struct adfs_discrecord **drp)
+{
+	struct adfs_discrecord *dr;
+
+	/* Do some sanity checks on the ADFS disc record */
+	dr = (struct adfs_discrecord *)(bh->b_data + 4);
+	if (adfs_checkdiscrecord(dr) || dr->nzones_high || dr->nzones != 1)
+		return -EILSEQ;
+
+	*drp = dr;
+	return 0;
+}
+
 static int adfs_fill_super(struct super_block *sb, void *data, int silent)
 {
 	struct adfs_discrecord *dr;
@@ -376,7 +390,9 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent)
 		goto error;
 
 	/* Try to probe the filesystem boot block */
-	ret = adfs_probe(sb, ADFS_DISCRECORD, silent, adfs_validate_bblk);
+	ret = adfs_probe(sb, ADFS_DISCRECORD, 1, adfs_validate_bblk);
+	if (ret == -EILSEQ)
+		ret = adfs_probe(sb, 0, silent, adfs_validate_dr0);
 	if (ret == -EILSEQ) {
 		if (!silent)
 			adfs_msg(sb, KERN_ERR,
Signature · 1 line
2.20.1
#45 lore raw

PATCH 40/41 fs/adfs: mostly divorse inode number from indirect disc address

In reply to Russell King - ARM Linux admin

Avoid using the inode number as the indirect disc address, even though these currently have the same value.

---
 fs/adfs/adfs.h  | 1 +
 fs/adfs/dir.c   | 4 ++--
 fs/adfs/inode.c | 6 ++++--
 3 files changed, 7 insertions(+), 4 deletions(-)
fs/adfs/adfs.h +1 -0
index c05555252fec..699c4fa8b78b 100644
@@ -32,6 +32,7 @@ static inline u16 adfs_filetype(u32 loadaddr)
 struct adfs_inode_info {
 	loff_t		mmu_private;
 	__u32		parent_id;	/* parent indirect disc address	*/
+	__u32		indaddr;	/* object indirect disc address	*/
 	__u32		loadaddr;	/* RISC OS load address		*/
 	__u32		execaddr;	/* RISC OS exec address		*/
 	unsigned int	attr;		/* RISC OS permissions		*/
fs/adfs/dir.c +2 -2
index dd940f17767d..77fbd196008f 100644
@@ -158,7 +158,7 @@ static int adfs_dir_read_inode(struct super_block *sb, struct inode *inode,
 {
 	int ret;
 
-	ret = adfs_dir_read(sb, inode->i_ino, inode->i_size, dir);
+	ret = adfs_dir_read(sb, ADFS_I(inode)->indaddr, inode->i_size, dir);
 	if (ret)
 		return ret;
 
@@ -372,7 +372,7 @@ static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
 			break;
 		}
 	}
-	obj->parent_id = inode->i_ino;
+	obj->parent_id = ADFS_I(inode)->indaddr;
 
 unlock_relse:
 	up_read(&adfs_dir_rwsem);
fs/adfs/inode.c +4 -2
index 212a56fc7911..32620f4a7623 100644
@@ -20,7 +20,8 @@ adfs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh,
 		if (block >= inode->i_blocks)
 			goto abort_toobig;
 
-		block = __adfs_block_map(inode->i_sb, inode->i_ino, block);
+		block = __adfs_block_map(inode->i_sb, ADFS_I(inode)->indaddr,
+					 block);
 		if (block)
 			map_bh(bh, inode->i_sb, block);
 		return 0;
@@ -259,6 +260,7 @@ adfs_iget(struct super_block *sb, struct object_info *obj)
 	 * for cross-directory renames.
 	 */
 	ADFS_I(inode)->parent_id = obj->parent_id;
+	ADFS_I(inode)->indaddr   = obj->indaddr;
 	ADFS_I(inode)->loadaddr  = obj->loadaddr;
 	ADFS_I(inode)->execaddr  = obj->execaddr;
 	ADFS_I(inode)->attr      = obj->attr;
@@ -353,7 +355,7 @@ int adfs_write_inode(struct inode *inode, struct writeback_control *wbc)
 	struct object_info obj;
 	int ret;
 
-	obj.indaddr	= inode->i_ino;
+	obj.indaddr	= ADFS_I(inode)->indaddr;
 	obj.name_len	= 0;
 	obj.parent_id	= ADFS_I(inode)->parent_id;
 	obj.loadaddr	= ADFS_I(inode)->loadaddr;
Signature · 1 line
2.20.1
#46 lore raw

PATCH 41/41 Documentation: update adfs filesystem documentation

In reply to Russell King - ARM Linux admin

Add an introduction to adfs to its documentation detailing which formats are supported by the module.

---
 Documentation/filesystems/adfs.txt | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)
Documentation/filesystems/adfs.txt +24 -0
index 5949766353f7..0baa8e8c1fc1 100644
@@ -1,3 +1,27 @@
+Filesystems supported by ADFS
+-----------------------------
+
+The ADFS module supports the following Filecore formats which have:
+
+- new maps
+- new directories or big directories
+
+In terms of the named formats, this means we support:
+
+- E and E+, with or without boot block
+- F and F+
+
+We fully support reading files from these filesystems, and writing to
+existing files within their existing allocation.  Essentially, we do
+not support changing any of the filesystem metadata.
+
+This is intended to support loopback mounted Linux native filesystems
+on a RISC OS Filecore filesystem, but will allow the data within files
+to be changed.
+
+If write support (ADFS_FS_RW) is configured, we allow rudimentary
+directory updates, specifically updating the access mode and timestamp.
+
 Mount options for ADFS
 ----------------------
 
Signature · 1 line
2.20.1