(Scripted) Merge in scripted filesystem ->d_inode to d_inode() conversions

Scripted merge in of scripted filesystem ->d_inode to d_inode() conversions
using the following perl script:

#!/usr/bin/perl -w
use strict;

open(my $fd, "<$0") || die $0;
my @script = <$fd>;
close($fd);

my @file_system_types;
open(my $g, 'git grep -l "struct file_system_type.*=" |') ||
    die "Can't grep for filesystem type";
@file_system_types = <$g>;
close($g);

my @excludes = (
    "fs/attr.c",
    "fs/dcache.c",
    "fs/exportfs/expfs.c",
    "fs/file_table.c",
    "fs/notify/",
    "fs/locks.c",
    "fs/namei.c",
    "fs/namespace.c",
    "fs/open.c",
    "fs/overlayfs/",
    "fs/utimes.c",
    "fs/xattr.c",
    "include/linux/",
    "Documentation/filesystems/vfs.txt",
    );

my @treat_as_fs = (
    "drivers/staging/lustre",
    "fs/kernfs",
    "fs/libfs.c",
    "fs/quota/dquot.c",
    "ipc",
    "kernel/relay.c",
    "kernel/trace",
    );

###############################################################################
#
# Find the filesystems and classify them according to whether they occupy a
# directory or a file in the source.
#
###############################################################################
my %fs_names = ();
my %fs_dirs = ();
my %fs_files = ();

# Miscellaneous convenience sets
my %fs_misc = (
#    "arch" => [],
#    "drivers" => [],
#    "fs" => [],
#    "security" => []
    );

my @fs_single = ();

fs_file: foreach my $file (@file_system_types) {
    chomp $file;
    foreach my $ex (@excludes, @treat_as_fs) {
	next fs_file if (substr($file, 0, length($ex)) eq $ex);
    }

    # Handle whole-directory filesystems
    if ($file =~ m!^fs/([a-z0-9]+)/.*[.]c!) {
	my $dir = substr($file, 0, rindex($file, "/"));
	my $name = $1;
	$fs_names{$name} = $dir;
	$fs_dirs{$dir} = [];
	next;
    }

    #next if ($file =~ m!^drivers/staging/lustre!);

    # Handle single-file filesystems
    $fs_files{$file} = [];
}

foreach my $path (@treat_as_fs) {
    if ($path =~ /[.][ch]/) {
	$fs_files{$path} = [];
    } else {
	my $name = substr($path, rindex($path, "/") + 1);
	$fs_names{$name} = $path;
	$fs_dirs{$path} = [];
    }
}

my @to_fs_inode = sort(keys(%fs_dirs));

###############################################################################
#
# Find all occurrences of files containing "->d_inode" and divide them amongst
# the various filesystems and non-filesystems.
#
###############################################################################
my @occurrences;
open($g, 'git grep -l "[-]>d_inode" |') ||
    die "Can't grep for ->d_inode";
@occurrences = <$g>;
close($g);

my %non_fs = ();

file: foreach my $file (@occurrences) {
    chomp $file;
    foreach my $ex (@excludes) {
	next file if (substr($file, 0, length($ex)) eq $ex);
    }

    foreach my $path (@to_fs_inode) {
	if (index($file, $path) == 0) {
	    #print $file, " found in ", $path, "\n";
	    push @{$fs_dirs{$path}}, $file;
	    next file;
	}
    }

    if (exists($fs_files{$file})) {
	foreach my $path (keys(%fs_misc)) {
	    if (index($file, $path) == 0) {
		push @{$fs_misc{$path}}, $file;
		delete $fs_files{$file};
		next file;
	    }
	}
    }

    if (exists($fs_files{$file})) {
	push @{$fs_files{$file}}, $file;
	next file;
    }

    if ($file =~ m!include/trace/events/([_a-zA-Z0-9]+)[.]h!) {
	my $fs = $1;
	if (exists($fs_names{$fs})) {
	    push @{$fs_dirs{$fs_names{$fs}}}, $file;
	    next;
	}
    }

    #print $file, " not found\n";
    $non_fs{$file} = [ $file ];
}

foreach my $path (sort(keys(%fs_files))) {
    push @fs_single, @{$fs_files{$path}};
}

###############################################################################
#
# Summarise how the filesystem file sets will be split up
#
###############################################################################
my $summarise = 0;
if ($summarise) {
    foreach my $path (sort(keys(%fs_dirs))) {
	print $path, ":\n";
	foreach my $file (@{$fs_dirs{$path}}) {
	    print "\t", $file, "\n";
	}
    }

    foreach my $path (sort(keys(%fs_misc))) {
	print $path, "-single-fs:\n";
	foreach my $file (@{$fs_misc{$path}}) {
	    print "\t", $file, "\n";
	}
    }

    print "single-fs:\n";
    foreach my $path (sort(keys(%fs_files))) {
	foreach my $file (@{$fs_files{$path}}) {
	    print "\t", $file, "\n";
	}
    }

    print "non-filesystem:\n";
    foreach my $path (sort(keys(%non_fs))) {
	foreach my $file (@{$non_fs{$path}}) {
	    print "\t", $file, "\n";
	}
    }

    print "\n";
}

###############################################################################
#
# Group the non-filesystems by directories with two or more files that need
# changing.
#
###############################################################################
my %non_groups = ();
my %non_dirs = ();

foreach my $file (keys(%non_fs)) {
    my $p = index($file, "/");
    my $q = index($file, "/", $p + 1);
    $p = $q if ($q != -1);
    my $dir = substr($file, 0, $p);
    $non_dirs{$dir} = 0 unless exists $non_dirs{$dir};
    $non_dirs{$dir}++;
    $non_groups{$dir} = [] unless exists $non_groups{$dir};
    push @{$non_groups{$dir}}, $file;
}

foreach my $dir (sort(keys(%non_dirs))) {
    #print $dir, " -> ", $non_dirs{$dir}, "\n";
    if ($non_dirs{$dir} == 1) {
	my $p = index($dir, "/");
	if ($p != -1) {
	    my $top = substr($dir, 0, $p);
	    $non_dirs{$top} = 0 unless exists $non_dirs{$top};
	    $non_dirs{$top}++;
	    $non_groups{$top} = [] unless exists $non_groups{$top};
	    push @{$non_groups{$top}}, @{$non_groups{$dir}};
	    delete $non_dirs{$dir};
	}
    }
}

#foreach my $dir (sort(keys(%non_dirs))) {
#    print "Non-filesystem ", $dir, ":\n";
#    foreach my $file (@{$non_groups{$dir}}) {
#	print "\t", $file, "\n";
#    }
#}

###############################################################################
#
# Set up the integration branch
#
###############################################################################
system("git", "checkout", "file-pin") == 0 || die;
die if `stg branch` ne "file-pin\n";
system("git", "reset", "--hard", "file-pin-devel") == 0 || die;

###############################################################################
#
# Fabricate commits for d_inode -> d_inode() conversion
#
###############################################################################
system("git", "checkout", "file-pin-fs-experimental") == 0 || die;
die if `stg branch` ne "file-pin-fs-experimental\n";
system("git", "reset", "--hard", "file-pin") == 0 || die;

sub convert_to_d_inode($$)
{
    my ($title, $files) = @_;

    unless (@{$files}) {
	print "Skipping $title with no files\n";
	return;
    }

    print "Process $title\n";

    my $dir = $files->[0];
    $dir =~ s![^/]+$!!;
    $dir =~ s!/$!!;
    $dir =~ s!/!_!g;

    foreach my $file (@{$files}) {
	open(my $fd, "<$file") || die $file;
	my @lines = <$fd>;
	close($fd);

	my @out = map {
	    # Convert ->d_inode to d_inode[_rcu]()
	    s!ACCESS_ONCE[(](([_a-zA-Z][_a-zA-Z0-9]*(->|[.]))*[_a-zA-Z][_a-zA-Z0-9]*)->d_inode[)]!d_inode_rcu($1)!g;
	    s!(([_a-zA-Z][_a-zA-Z0-9]*(->|[.]))*[_a-zA-Z][_a-zA-Z0-9]*)->d_inode!d_inode($1)!g;

	    # Convert some d_inode() to d_really_is_xxx()
	    s!(if\s*[(].*)\bd_inode[(]([a-z_A-Z0-9->.]+)[)] == NULL!$1d_really_is_negative($2)!g;
	    s/(if\s*[(].*)\bd_inode[(]([a-z_A-Z0-9->.]+)[)] != NULL/$1d_really_is_positive($2)/g;
	    s/(if\s*[(])!d_inode[(]([a-z_A-Z0-9->.]+)[)][)]/$1d_really_is_negative($2))/g;
	    s/(if\s*[(])d_inode[(]([a-z_A-Z0-9->.]+)[)][)]/$1d_really_is_positive($2))/g;
	    s/(if\s*[(].*)!d_inode[(]([a-z_A-Z0-9->.]+)[)][)]$/$1d_really_is_negative($2))/g;
	    s/(if\s*[(].*)\bd_inode[(]([a-z_A-Z0-9->.]+)[)][)]$/$1d_really_is_positive($2))/g;
	    s/(if\s*[(].*)!d_inode[(]([a-z_A-Z0-9->.]+)[)][)] [{]$/$1d_really_is_negative($2)) {/g;
	    s/(if\s*[(].*)\bd_inode[(]([a-z_A-Z0-9->.]+)[)][)] [{]$/$1d_really_is_positive($2)) {/g;
	    s/!d_inode[(]([a-z_A-Z0-9->.]+)[)]\s*([&][&]|[|][|])/d_really_is_negative($1) $2/g;
	    s/([^a-z_0-9A-Z])d_inode[(]([a-z_A-Z0-9->.]+)[)]\s*([&][&]|[|][|])/$1d_really_is_positive($2) $3/g;

	    # Handle cases of == or != d_inode() getting converted incorrectly
	    s/([=!]=\s*)d_really_is_positive/$1d_inode/g;
	} @lines;

	open($fd, ">$file") || die $file;
	print $fd @lines;
	close($fd) || die $file;
    }

    system("git", "add", @{$files}) == 0 || die;
    system("git", "commit", "-m",
	   "VFS: (Scripted) Convert ->d_inode to d_inode() $title\n" .
	   "\n" .
	   'Signed-off-by: David Howells <dhowells@redhat.com>') == 0 || die;
}

foreach my $fs (sort(keys(%fs_dirs))) {
    convert_to_d_inode("in $fs/", $fs_dirs{$fs});
}

foreach my $fs (sort(keys(%fs_misc))) {
    convert_to_d_inode("in $fs/", $fs_misc{$fs});
}

#convert_to_fs_inode("miscellany", \@fs_single);
foreach my $file (sort(@fs_single)) {
    convert_to_d_inode("in $file", [$file]);
}

# Merge the changes back into the integration branch, noting the script in the
# merge message.
my @msg = (
    "(Scripted) Merge in scripted filesystem ->d_inode to d_inode() conversions\n",
    "\n",
    "Scripted merge in of scripted filesystem ->d_inode to d_inode() conversions\n",
    "using the following perl script:\n",
    "\n",
    @script,
    "\n",
    'Signed-off-by: David Howells <dhowells@redhat.com>');

system("git", "checkout", "file-pin") == 0 || die;
die if `stg branch` ne "file-pin\n";
system("git", "merge", "--no-ff", "file-pin-fs-experimental", "-m", join("", @msg));

###############################################################################
#
# Fabricate an stg commit for d_inode -> d_backing_inode() conversion
#
###############################################################################
system("git", "checkout", "file-pin-nonfs-experimental") == 0 || die;
die if `stg branch` ne "file-pin-nonfs-experimental\n";
system("git", "reset", "--hard", "file-pin") == 0 || die;

sub convert_to_d_backing_inode($$)
{
    my ($title, $files) = @_;

    unless (@{$files}) {
	print "Skipping $title with no files\n";
	return;
    }

    print "Process $title\n";

    my $dir = $files->[0];
    $dir =~ s![^/]+$!!;
    $dir =~ s!/$!!;
    $dir =~ s!/!_!g;

    foreach my $file (@{$files}) {
	open(my $fd, "<$file") || die $file;
	my @lines = <$fd>;
	close($fd);

	my @out = map {
	    # Convert ->d_inode to d_inode[_rcu]()
	    s!ACCESS_ONCE[(](([_a-zA-Z][_a-zA-Z0-9]*(->|[.]))*[_a-zA-Z][_a-zA-Z0-9]*)->d_inode[)]!d_backing_inode_rcu($1)!g;
	    s!(([_a-zA-Z][_a-zA-Z0-9]*(->|[.]))*[_a-zA-Z][_a-zA-Z0-9]*)->d_inode!d_backing_inode($1)!g;
	    # Convert some d_inode() to d_is_xxx()
	    s!(if\s*[(].*)\bd_backing_inode[(]([a-z_A-Z0-9->.]+)[)] == NULL!$1d_is_negative($2)!g;
	    s/(if\s*[(].*)\bd_backing_inode[(]([a-z_A-Z0-9->.]+)[)] != NULL/$1d_is_positive($2)/g;
	    s/(if\s*[(])!d_backing_inode[(]([a-z_A-Z0-9->.]+)[)][)]/$1d_is_negative($2))/g;
	    s/(if\s*[(])d_backing_inode[(]([a-z_A-Z0-9->.]+)[)][)]/$1d_is_positive($2))/g;
	    s/(if\s*[(].*)!d_backing_inode[(]([a-z_A-Z0-9->.]+)[)][)]$/$1d_is_negative($2))/g;
	    s/(if\s*[(].*)\bd_backing_inode[(]([a-z_A-Z0-9->.]+)[)][)]$/$1d_is_positive($2))/g;
	    s/(if\s*[(].*)!d_backing_inode[(]([a-z_A-Z0-9->.]+)[)][)] [{]$/$1d_is_negative($2)) {/g;
	    s/(if\s*[(].*)\bd_backing_inode[(]([a-z_A-Z0-9->.]+)[)][)] [{]$/$1d_is_positive($2)) {/g;
	    s/!d_backing_inode[(]([a-z_A-Z0-9->.]+)[)]\s*([&][&]|[|][|])/d_is_negative($1) $2/g;
	    s/([^a-z_0-9A-Z])d_backing_inode[(]([a-z_A-Z0-9->.]+)[)]\s*([&][&]|[|][|])/$1d_is_positive($2) $3/g;
	} @lines;

	open($fd, ">$file") || die $file;
	print $fd @lines;
	close($fd) || die $file;
    }

    system("git", "add", @{$files}) == 0 || die;
    system("git", "commit", "-m",
	   "VFS: (Scripted) Convert ->d_inode to d_backing_inode() $title\n" .
	   "\n" .
	   'Signed-off-by: David Howells <dhowells@redhat.com>') == 0 || die;
}

foreach my $dir (sort(keys(%non_dirs))) {
    convert_to_d_backing_inode("in $dir", $non_groups{$dir});
}

# Merge the changes back into the integration branch, noting the script in the
# merge message.
@msg = (
    "(Scripted) Merge in scripted non-filesystem ->d_inode to d_backing_inode() conversions\n",
    "\n",
    "Scripted merge in of scripted non-filesystem ->d_inode to d_backing_inode() conversions\n",
    "using the following perl script:\n",
    "\n",
    @script,
    "\n",
    'Signed-off-by: David Howells <dhowells@redhat.com>');

system("git", "checkout", "file-pin") == 0 || die;
die if `stg branch` ne "file-pin\n";
system("git", "merge", "--no-ff", "file-pin-nonfs-experimental", "-m", join("", @msg));

Signed-off-by: David Howells <dhowells@redhat.com>