xref: /linux/fs/smb/client/gen_smb1_mapping (revision 81dc1e4d32b064ac47abc60b0acbf49b66a34d52)
1#!/usr/bin/perl -w
2# SPDX-License-Identifier: GPL-2.0-or-later
3#
4# Script to generate SMB1 error mapping tables.
5#
6# Copyright (C) 2026 KylinSoft Co., Ltd. All rights reserved.
7# Author(s): Huiwen He <hehuiwen@kylinos.cn>
8#            ChenXiaoSong <chenxiaosong@kylinos.cn>
9#
10use strict;
11
12if ($#ARGV != 1) {
13	print STDERR "Usage: $0 <in-file> <out-file>\n";
14	exit(2);
15}
16
17# Parse input parameters and extract filenames
18my $in_file  = $ARGV[0];
19my $out_file = $ARGV[1];
20my $input_name  = (split m|/|, $in_file)[-1];
21my $output_name = (split m|/|, $out_file)[-1];
22my $script_name = (split m|/|, $0)[-1];
23my @list     = ();
24my %seen     = ();
25my $current_class = "";
26
27# Parse annotated entries from the input file
28open(my $in, "<", $in_file) or die "Cannot open $in_file: $!";
29if ($in_file =~ /nterr\.h$/) {
30	while (<$in>) {
31		# Handle backslash line continuation
32		$_ .= <$in> while s/\\\s*\n//;
33
34		# Match #define NT_STATUS_... followed by // CLASS, CODE or /* CLASS, CODE */
35		my $re = qr{^\s*#define\s+(NT_STATUS_[A-Za-z0-9_]+)\s+(.+?)\s*} .
36			 qr{(?://\s*|/\*\s*)([A-Z0-9_]+)\s*,\s*([A-Za-z0-9_]+)};
37
38		if (/$re/) {
39			my ($name, $val_str, $class, $code) = ($1, $2, $3, $4);
40
41			# Skip duplicate macro names
42			next if $seen{$name}++;
43
44			# Clean up value string (remove parens, spaces)
45			$val_str =~ s/[\s\(\)]//g;
46			my $val = 0;
47			foreach my $part (split(/\|/, $val_str)) {
48				$val |= hex($part);
49			}
50			push @list, { val => $val, name => $name, class => $class, code => $code };
51		} elsif (/^\s*#define\s+NT_STATUS_.*(?:\/\/|\/\*)/) {
52			# Error if macro has a comment (// or /*) but fails mapping format
53			die "Error: Invalid mapping comment format in $in_file: $_";
54		}
55	}
56} elsif ($in_file =~ /smberr\.h$/) {
57	while (<$in>) {
58		# Handle backslash line continuation
59		$_ .= <$in> while s/\\\s*\n//;
60
61		# Detect current error class from header comments (ERRDOS or ERRSRV)
62		if (/generated with the (\w+) error class/) {
63			$current_class = $1;
64		}
65
66		# Match #define ERR/Err_... <value> followed by // -POSIX_ERR or /* -POSIX_ERR */
67		if (/^\s*#define\s+((?:ERR|Err)[A-Za-z0-9_]+)\s+([0-9a-fA-FxX]+)\s*(?:\/\/|\/\*)\s*(-[A-Z0-9_]+)/) {
68			my ($name, $val_str, $error) = ($1, $2, $3);
69			my $val = ($val_str =~ /^0x/i) ? hex($val_str) : $val_str;
70			push @list, { val => $val, name => $name, error => $error, class => $current_class };
71		} elsif ($current_class && /^\s*#define\s+(?:ERR|Err).*?(?:\/\/|\/\*)/) {
72			# Error if macro has a comment (// or /*) but fails mapping format
73			die "Error: Invalid mapping comment format in $in_file: $_";
74		}
75	}
76}
77close($in);
78
79# Fail if no entries were found to avoid broken builds
80die "Error: No mapping entries found in $in_file\n" unless @list;
81
82# Sort entries numerically by value
83@list = sort { $a->{val} <=> $b->{val} } @list;
84
85# Generate the C mapping table output file
86open(my $out, ">", $out_file) or die "Cannot open $out_file: $!";
87print $out "/* Autogenerated from $input_name by $script_name */\n\n";
88
89if ($output_name eq "smb1_mapping_table.c") {
90	# Generate NT status -> DOS error mapping file
91
92	my $count = scalar @list;
93	my $full_names = "";
94
95	for (my $i = 0; $i < $count; $i++) {
96		my $e = $list[$i];
97		my $val = $e->{val};
98
99		$full_names .= $e->{name};
100
101		# Merge synonyms
102		if ($i < $count - 1 && $list[$i + 1]->{val} == $val) {
103			$full_names .= " or ";
104			next;
105		}
106
107		printf $out "\t{ %s, %s, 0x%08x, \"%s\" },\n", $e->{class}, $e->{code}, $val, $full_names;
108
109		$full_names = "";
110	}
111} elsif ($output_name eq "smb1_err_dos_map.c" || $output_name eq "smb1_err_srv_map.c") {
112	# Generate SMB1 error -> POSIX error mapping file
113
114	# Filtered by exact output filename
115	my $filter = ($output_name eq "smb1_err_dos_map.c") ? "ERRDOS" : "ERRSRV";
116	foreach my $e (@list) {
117		if (!$filter || $e->{class} eq $filter) {
118			printf $out "\t{%s, %s},\n", $e->{name}, $e->{error};
119		}
120	}
121} else {
122	die "Error: Unsupported output target: $output_name\n";
123}
124close($out);
125