summaryrefslogtreecommitdiff
path: root/fs/smb/client/gen_smb1_mapping
blob: c2b2939a83c6f8d5612fa3bc9bcabe685fd8228d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#!/usr/bin/perl -w
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Script to generate SMB1 error mapping tables.
#
# Copyright (C) 2026 KylinSoft Co., Ltd. All rights reserved.
# Author(s): Huiwen He <hehuiwen@kylinos.cn>
#            ChenXiaoSong <chenxiaosong@kylinos.cn>
#
use strict;

if ($#ARGV != 1) {
	print STDERR "Usage: $0 <in-file> <out-file>\n";
	exit(2);
}

# Parse input parameters and extract filenames
my $in_file  = $ARGV[0];
my $out_file = $ARGV[1];
my $input_name  = (split m|/|, $in_file)[-1];
my $output_name = (split m|/|, $out_file)[-1];
my $script_name = (split m|/|, $0)[-1];
my @list     = ();
my %seen     = ();
my $current_class = "";

# Parse annotated entries from the input file
open(my $in, "<", $in_file) or die "Cannot open $in_file: $!";
if ($in_file =~ /nterr\.h$/) {
	while (<$in>) {
		# Handle backslash line continuation
		$_ .= <$in> while s/\\\s*\n//;

		# Match #define NT_STATUS_... followed by // CLASS, CODE or /* CLASS, CODE */
		my $re = qr{^\s*#define\s+(NT_STATUS_[A-Za-z0-9_]+)\s+(.+?)\s*} .
			 qr{(?://\s*|/\*\s*)([A-Z0-9_]+)\s*,\s*([A-Za-z0-9_]+)};

		if (/$re/) {
			my ($name, $val_str, $class, $code) = ($1, $2, $3, $4);

			# Skip duplicate macro names
			next if $seen{$name}++;

			# Clean up value string (remove parens, spaces)
			$val_str =~ s/[\s\(\)]//g;
			my $val = 0;
			foreach my $part (split(/\|/, $val_str)) {
				$val |= hex($part);
			}
			push @list, { val => $val, name => $name, class => $class, code => $code };
		} elsif (/^\s*#define\s+NT_STATUS_.*(?:\/\/|\/\*)/) {
			# Error if macro has a comment (// or /*) but fails mapping format
			die "Error: Invalid mapping comment format in $in_file: $_";
		}
	}
} elsif ($in_file =~ /smberr\.h$/) {
	while (<$in>) {
		# Handle backslash line continuation
		$_ .= <$in> while s/\\\s*\n//;

		# Detect current error class from header comments (ERRDOS or ERRSRV)
		if (/generated with the (\w+) error class/) {
			$current_class = $1;
		}

		# Match #define ERR/Err_... <value> followed by // -POSIX_ERR or /* -POSIX_ERR */
		if (/^\s*#define\s+((?:ERR|Err)[A-Za-z0-9_]+)\s+([0-9a-fA-FxX]+)\s*(?:\/\/|\/\*)\s*(-[A-Z0-9_]+)/) {
			my ($name, $val_str, $error) = ($1, $2, $3);
			my $val = ($val_str =~ /^0x/i) ? hex($val_str) : $val_str;
			push @list, { val => $val, name => $name, error => $error, class => $current_class };
		} elsif ($current_class && /^\s*#define\s+(?:ERR|Err).*?(?:\/\/|\/\*)/) {
			# Error if macro has a comment (// or /*) but fails mapping format
			die "Error: Invalid mapping comment format in $in_file: $_";
		}
	}
}
close($in);

# Fail if no entries were found to avoid broken builds
die "Error: No mapping entries found in $in_file\n" unless @list;

# Sort entries numerically by value
@list = sort { $a->{val} <=> $b->{val} } @list;

# Generate the C mapping table output file
open(my $out, ">", $out_file) or die "Cannot open $out_file: $!";
print $out "/* Autogenerated from $input_name by $script_name */\n\n";

if ($output_name eq "smb1_mapping_table.c") {
	# Generate NT status -> DOS error mapping file

	my $count = scalar @list;
	my $full_names = "";

	for (my $i = 0; $i < $count; $i++) {
		my $e = $list[$i];
		my $val = $e->{val};

		$full_names .= $e->{name};

		# Merge synonyms
		if ($i < $count - 1 && $list[$i + 1]->{val} == $val) {
			$full_names .= " or ";
			next;
		}

		printf $out "\t{ %s, %s, 0x%08x, \"%s\" },\n", $e->{class}, $e->{code}, $val, $full_names;

		$full_names = "";
	}
} elsif ($output_name eq "smb1_err_dos_map.c" || $output_name eq "smb1_err_srv_map.c") {
	# Generate SMB1 error -> POSIX error mapping file

	# Filtered by exact output filename
	my $filter = ($output_name eq "smb1_err_dos_map.c") ? "ERRDOS" : "ERRSRV";
	foreach my $e (@list) {
		if (!$filter || $e->{class} eq $filter) {
			printf $out "\t{%s, %s},\n", $e->{name}, $e->{error};
		}
	}
} else {
	die "Error: Unsupported output target: $output_name\n";
}
close($out);