xref: /linux/fs/smb/client/gen_smb1_mapping (revision 81dc1e4d32b064ac47abc60b0acbf49b66a34d52)
1415c5b8cSHuiwen He#!/usr/bin/perl -w
2415c5b8cSHuiwen He# SPDX-License-Identifier: GPL-2.0-or-later
3415c5b8cSHuiwen He#
4415c5b8cSHuiwen He# Script to generate SMB1 error mapping tables.
5415c5b8cSHuiwen He#
6415c5b8cSHuiwen He# Copyright (C) 2026 KylinSoft Co., Ltd. All rights reserved.
7415c5b8cSHuiwen He# Author(s): Huiwen He <hehuiwen@kylinos.cn>
8415c5b8cSHuiwen He#            ChenXiaoSong <chenxiaosong@kylinos.cn>
9415c5b8cSHuiwen He#
10415c5b8cSHuiwen Heuse strict;
11415c5b8cSHuiwen He
12415c5b8cSHuiwen Heif ($#ARGV != 1) {
13415c5b8cSHuiwen He	print STDERR "Usage: $0 <in-file> <out-file>\n";
14415c5b8cSHuiwen He	exit(2);
15415c5b8cSHuiwen He}
16415c5b8cSHuiwen He
17415c5b8cSHuiwen He# Parse input parameters and extract filenames
18415c5b8cSHuiwen Hemy $in_file  = $ARGV[0];
19415c5b8cSHuiwen Hemy $out_file = $ARGV[1];
20415c5b8cSHuiwen Hemy $input_name  = (split m|/|, $in_file)[-1];
21415c5b8cSHuiwen Hemy $output_name = (split m|/|, $out_file)[-1];
22415c5b8cSHuiwen Hemy $script_name = (split m|/|, $0)[-1];
23415c5b8cSHuiwen Hemy @list     = ();
24415c5b8cSHuiwen Hemy %seen     = ();
25*58ac796bSHuiwen Hemy $current_class = "";
26415c5b8cSHuiwen He
27415c5b8cSHuiwen He# Parse annotated entries from the input file
28415c5b8cSHuiwen Heopen(my $in, "<", $in_file) or die "Cannot open $in_file: $!";
29415c5b8cSHuiwen Heif ($in_file =~ /nterr\.h$/) {
30415c5b8cSHuiwen He	while (<$in>) {
31415c5b8cSHuiwen He		# Handle backslash line continuation
32415c5b8cSHuiwen He		$_ .= <$in> while s/\\\s*\n//;
33415c5b8cSHuiwen He
34415c5b8cSHuiwen He		# Match #define NT_STATUS_... followed by // CLASS, CODE or /* CLASS, CODE */
35415c5b8cSHuiwen He		my $re = qr{^\s*#define\s+(NT_STATUS_[A-Za-z0-9_]+)\s+(.+?)\s*} .
36415c5b8cSHuiwen He			 qr{(?://\s*|/\*\s*)([A-Z0-9_]+)\s*,\s*([A-Za-z0-9_]+)};
37415c5b8cSHuiwen He
38415c5b8cSHuiwen He		if (/$re/) {
39415c5b8cSHuiwen He			my ($name, $val_str, $class, $code) = ($1, $2, $3, $4);
40415c5b8cSHuiwen He
41415c5b8cSHuiwen He			# Skip duplicate macro names
42415c5b8cSHuiwen He			next if $seen{$name}++;
43415c5b8cSHuiwen He
44415c5b8cSHuiwen He			# Clean up value string (remove parens, spaces)
45415c5b8cSHuiwen He			$val_str =~ s/[\s\(\)]//g;
46415c5b8cSHuiwen He			my $val = 0;
47415c5b8cSHuiwen He			foreach my $part (split(/\|/, $val_str)) {
48415c5b8cSHuiwen He				$val |= hex($part);
49415c5b8cSHuiwen He			}
50415c5b8cSHuiwen He			push @list, { val => $val, name => $name, class => $class, code => $code };
51415c5b8cSHuiwen He		} elsif (/^\s*#define\s+NT_STATUS_.*(?:\/\/|\/\*)/) {
52415c5b8cSHuiwen He			# Error if macro has a comment (// or /*) but fails mapping format
53415c5b8cSHuiwen He			die "Error: Invalid mapping comment format in $in_file: $_";
54415c5b8cSHuiwen He		}
55415c5b8cSHuiwen He	}
56*58ac796bSHuiwen He} elsif ($in_file =~ /smberr\.h$/) {
57*58ac796bSHuiwen He	while (<$in>) {
58*58ac796bSHuiwen He		# Handle backslash line continuation
59*58ac796bSHuiwen He		$_ .= <$in> while s/\\\s*\n//;
60*58ac796bSHuiwen He
61*58ac796bSHuiwen He		# Detect current error class from header comments (ERRDOS or ERRSRV)
62*58ac796bSHuiwen He		if (/generated with the (\w+) error class/) {
63*58ac796bSHuiwen He			$current_class = $1;
64*58ac796bSHuiwen He		}
65*58ac796bSHuiwen He
66*58ac796bSHuiwen He		# Match #define ERR/Err_... <value> followed by // -POSIX_ERR or /* -POSIX_ERR */
67*58ac796bSHuiwen He		if (/^\s*#define\s+((?:ERR|Err)[A-Za-z0-9_]+)\s+([0-9a-fA-FxX]+)\s*(?:\/\/|\/\*)\s*(-[A-Z0-9_]+)/) {
68*58ac796bSHuiwen He			my ($name, $val_str, $error) = ($1, $2, $3);
69*58ac796bSHuiwen He			my $val = ($val_str =~ /^0x/i) ? hex($val_str) : $val_str;
70*58ac796bSHuiwen He			push @list, { val => $val, name => $name, error => $error, class => $current_class };
71*58ac796bSHuiwen He		} elsif ($current_class && /^\s*#define\s+(?:ERR|Err).*?(?:\/\/|\/\*)/) {
72*58ac796bSHuiwen He			# Error if macro has a comment (// or /*) but fails mapping format
73*58ac796bSHuiwen He			die "Error: Invalid mapping comment format in $in_file: $_";
74*58ac796bSHuiwen He		}
75*58ac796bSHuiwen He	}
76415c5b8cSHuiwen He}
77415c5b8cSHuiwen Heclose($in);
78415c5b8cSHuiwen He
79415c5b8cSHuiwen He# Fail if no entries were found to avoid broken builds
80415c5b8cSHuiwen Hedie "Error: No mapping entries found in $in_file\n" unless @list;
81415c5b8cSHuiwen He
82415c5b8cSHuiwen He# Sort entries numerically by value
83415c5b8cSHuiwen He@list = sort { $a->{val} <=> $b->{val} } @list;
84415c5b8cSHuiwen He
85415c5b8cSHuiwen He# Generate the C mapping table output file
86415c5b8cSHuiwen Heopen(my $out, ">", $out_file) or die "Cannot open $out_file: $!";
87415c5b8cSHuiwen Heprint $out "/* Autogenerated from $input_name by $script_name */\n\n";
88415c5b8cSHuiwen He
89415c5b8cSHuiwen Heif ($output_name eq "smb1_mapping_table.c") {
90415c5b8cSHuiwen He	# Generate NT status -> DOS error mapping file
91415c5b8cSHuiwen He
92415c5b8cSHuiwen He	my $count = scalar @list;
93415c5b8cSHuiwen He	my $full_names = "";
94415c5b8cSHuiwen He
95415c5b8cSHuiwen He	for (my $i = 0; $i < $count; $i++) {
96415c5b8cSHuiwen He		my $e = $list[$i];
97415c5b8cSHuiwen He		my $val = $e->{val};
98415c5b8cSHuiwen He
99415c5b8cSHuiwen He		$full_names .= $e->{name};
100415c5b8cSHuiwen He
101415c5b8cSHuiwen He		# Merge synonyms
102415c5b8cSHuiwen He		if ($i < $count - 1 && $list[$i + 1]->{val} == $val) {
103415c5b8cSHuiwen He			$full_names .= " or ";
104415c5b8cSHuiwen He			next;
105415c5b8cSHuiwen He		}
106415c5b8cSHuiwen He
107415c5b8cSHuiwen He		printf $out "\t{ %s, %s, 0x%08x, \"%s\" },\n", $e->{class}, $e->{code}, $val, $full_names;
108415c5b8cSHuiwen He
109415c5b8cSHuiwen He		$full_names = "";
110415c5b8cSHuiwen He	}
111*58ac796bSHuiwen He} elsif ($output_name eq "smb1_err_dos_map.c" || $output_name eq "smb1_err_srv_map.c") {
112*58ac796bSHuiwen He	# Generate SMB1 error -> POSIX error mapping file
113*58ac796bSHuiwen He
114*58ac796bSHuiwen He	# Filtered by exact output filename
115*58ac796bSHuiwen He	my $filter = ($output_name eq "smb1_err_dos_map.c") ? "ERRDOS" : "ERRSRV";
116*58ac796bSHuiwen He	foreach my $e (@list) {
117*58ac796bSHuiwen He		if (!$filter || $e->{class} eq $filter) {
118*58ac796bSHuiwen He			printf $out "\t{%s, %s},\n", $e->{name}, $e->{error};
119*58ac796bSHuiwen He		}
120*58ac796bSHuiwen He	}
121*58ac796bSHuiwen He} else {
122*58ac796bSHuiwen He	die "Error: Unsupported output target: $output_name\n";
123415c5b8cSHuiwen He}
124415c5b8cSHuiwen Heclose($out);
125