mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-08-29 03:51:54 +08:00
fix(glance): support prob iso image (#24133)
This commit is contained in:
@@ -2,7 +2,6 @@ package llm
|
||||
|
||||
import (
|
||||
"yunion.io/x/onecloud/cmd/climc/shell"
|
||||
|
||||
modules "yunion.io/x/onecloud/pkg/mcclient/modules/llm"
|
||||
commonoptions "yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
options "yunion.io/x/onecloud/pkg/mcclient/options/llm"
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"yunion.io/x/pkg/util/shellutils"
|
||||
"yunion.io/x/structarg"
|
||||
|
||||
_ "yunion.io/x/onecloud/cmd/isocli/shell"
|
||||
)
|
||||
|
||||
type BaseOptions struct {
|
||||
Debug bool `help:"debug mode"`
|
||||
SUBCOMMAND string `help:"aliyuncli subcommand" subcommand:"true"`
|
||||
}
|
||||
|
||||
func getSubcommandParser() (*structarg.ArgumentParser, error) {
|
||||
parse, e := structarg.NewArgumentParserWithHelp(&BaseOptions{},
|
||||
"isocli",
|
||||
"Command-line ISO tools.",
|
||||
`See "isocli COMMAND --help" for help on a specific command.`)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
subcmd := parse.GetSubcommand()
|
||||
if subcmd == nil {
|
||||
return nil, fmt.Errorf("No subcommand argument.")
|
||||
}
|
||||
for _, v := range shellutils.CommandTable {
|
||||
_, e := subcmd.AddSubParserWithHelp(v.Options, v.Command, v.Desc, v.Callback)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
}
|
||||
return parse, nil
|
||||
}
|
||||
|
||||
func showErrorAndExit(e error) {
|
||||
fmt.Fprintf(os.Stderr, "%s", e)
|
||||
fmt.Fprintln(os.Stderr)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func main() {
|
||||
parser, e := getSubcommandParser()
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
e = parser.ParseArgs(os.Args[1:], false)
|
||||
|
||||
if parser.IsHelpSet() {
|
||||
fmt.Print(parser.HelpString())
|
||||
return
|
||||
}
|
||||
subcmd := parser.GetSubcommand()
|
||||
subparser := subcmd.GetSubParser()
|
||||
if e != nil || subparser == nil {
|
||||
if subparser != nil {
|
||||
fmt.Print(subparser.Usage())
|
||||
} else {
|
||||
fmt.Print(parser.Usage())
|
||||
}
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
suboptions := subparser.Options()
|
||||
e = subcmd.Invoke(suboptions)
|
||||
if e != nil {
|
||||
showErrorAndExit(e)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package shell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/util/shellutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/isoutils"
|
||||
)
|
||||
|
||||
func init() {
|
||||
type DetectOSOptions struct {
|
||||
ISO string `help:"ISO file"`
|
||||
}
|
||||
shellutils.R(&DetectOSOptions{}, "detect", "Detect", func(args *DetectOSOptions) error {
|
||||
stat, err := os.Stat(args.ISO)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if stat.IsDir() {
|
||||
// 如果是目录,仅遍历一层目录下的 .iso 文件
|
||||
entries, err := os.ReadDir(args.ISO)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
continue // 跳过子目录
|
||||
}
|
||||
if strings.HasSuffix(strings.ToLower(entry.Name()), ".iso") {
|
||||
path := filepath.Join(args.ISO, entry.Name())
|
||||
fmt.Printf("Processing: %s\n", path)
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
fmt.Printf("Error opening %s: %v\n", path, err)
|
||||
continue // 继续处理其他文件
|
||||
}
|
||||
isoInfo, err := isoutils.DetectOSFromISO(f)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
fmt.Printf("Error detecting OS from %s: %v\n", path, err)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("%s: %s\n", path, jsonutils.Marshal(isoInfo))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 如果是文件,按原逻辑处理
|
||||
f, err := os.Open(args.ISO)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
isoInfo, err := isoutils.DetectOSFromISO(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s: %s\n", args.ISO, jsonutils.Marshal(isoInfo))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -6,6 +6,7 @@ require (
|
||||
bazil.org/fuse v0.0.0-20180421153158-65cc252bf669
|
||||
github.com/360EntSecGroup-Skylar/excelize v1.4.0
|
||||
github.com/LeeEirc/terminalparser v0.0.0-20240205084113-fbf78c8480f2
|
||||
github.com/Microsoft/go-winio v0.6.2
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.684
|
||||
github.com/anacrolix/torrent v1.57.0
|
||||
github.com/benbjohnson/clock v1.0.0
|
||||
@@ -39,6 +40,7 @@ require (
|
||||
github.com/influxdata/influxql v1.1.0
|
||||
github.com/influxdata/promql/v2 v2.12.0
|
||||
github.com/jaypipes/ghw v0.11.0
|
||||
github.com/kdomanski/iso9660 v0.4.0
|
||||
github.com/koding/websocketproxy v0.0.0-20181220232114-7ed82d81a28c
|
||||
github.com/lestrrat-go/jwx v1.0.2
|
||||
github.com/lestrrat/go-jwx v0.0.0-20180221005942-b7d4802280ae
|
||||
@@ -52,6 +54,7 @@ require (
|
||||
github.com/miekg/dns v1.1.25
|
||||
github.com/minio/minio-go v6.0.14+incompatible
|
||||
github.com/mitchellh/go-wordwrap v1.0.1
|
||||
github.com/mogaika/udf v0.0.0-20171019171931-167f0ab01c73
|
||||
github.com/petermattis/goid v0.0.0-20250904145737-900bdf8bb490
|
||||
github.com/pierrec/lz4/v4 v4.1.15
|
||||
github.com/pkg/errors v0.9.1
|
||||
@@ -78,19 +81,21 @@ require (
|
||||
github.com/zexi/influxql-to-metricsql v0.1.1
|
||||
go.etcd.io/etcd/api/v3 v3.5.0
|
||||
go.etcd.io/etcd/client/v3 v3.5.0
|
||||
golang.org/x/crypto v0.21.0
|
||||
golang.org/x/net v0.23.0
|
||||
golang.org/x/sync v0.8.0
|
||||
golang.org/x/sys v0.26.0
|
||||
golang.org/x/text v0.14.0
|
||||
golang.org/x/crypto v0.41.0
|
||||
golang.org/x/net v0.43.0
|
||||
golang.org/x/sync v0.16.0
|
||||
golang.org/x/sys v0.35.0
|
||||
golang.org/x/text v0.28.0
|
||||
golang.org/x/time v0.5.0
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230215201556-9c5414ab4bde
|
||||
google.golang.org/grpc v1.62.0
|
||||
google.golang.org/protobuf v1.35.1
|
||||
gopkg.in/fatih/set.v0 v0.2.1
|
||||
gopkg.in/ini.v1 v1.62.0
|
||||
gopkg.in/mail.v2 v2.3.1
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.19.3
|
||||
k8s.io/apimachinery v0.19.3
|
||||
k8s.io/client-go v0.19.3
|
||||
@@ -103,7 +108,7 @@ require (
|
||||
yunion.io/x/jsonutils v1.0.1-0.20250507052344-1abcf4f443b1
|
||||
yunion.io/x/log v1.0.1-0.20240305175729-7cf2d6cd5a91
|
||||
yunion.io/x/ovsdb v0.0.0-20230306173834-f164f413a900
|
||||
yunion.io/x/pkg v1.10.4-0.20251114095758-2a2f105d9712
|
||||
yunion.io/x/pkg v1.10.4-0.20260127060125-8939521ef75e
|
||||
yunion.io/x/s3cli v0.0.0-20241221171442-1c11599d28e1
|
||||
yunion.io/x/sqlchemy v1.1.3-0.20251231025938-b0a38f6e9fab
|
||||
yunion.io/x/structarg v0.0.0-20231017124457-df4d5009457c
|
||||
@@ -127,7 +132,6 @@ require (
|
||||
github.com/ClickHouse/clickhouse-go v1.5.4 // indirect
|
||||
github.com/DataDog/dd-trace-go v0.6.1 // indirect
|
||||
github.com/DataDog/zstd v1.3.4 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/Microsoft/hcsshim v0.8.14 // indirect
|
||||
github.com/RoaringBitmap/roaring v1.2.3 // indirect
|
||||
github.com/Shopify/sarama v1.20.0 // indirect
|
||||
@@ -371,7 +375,7 @@ require (
|
||||
go.uber.org/zap v1.17.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
||||
golang.org/x/oauth2 v0.17.0 // indirect
|
||||
golang.org/x/term v0.18.0 // indirect
|
||||
golang.org/x/term v0.34.0 // indirect
|
||||
google.golang.org/api v0.167.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
|
||||
@@ -379,8 +383,6 @@ require (
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240228224816-df926f6c8641 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 // indirect
|
||||
lukechampine.com/blake3 v1.1.6 // indirect
|
||||
modernc.org/libc v1.22.3 // indirect
|
||||
|
||||
@@ -656,6 +656,8 @@ github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uia
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
||||
github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw=
|
||||
github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
|
||||
github.com/kdomanski/iso9660 v0.4.0 h1:BPKKdcINz3m0MdjIMwS0wx1nofsOjxOq8TOr45WGHFg=
|
||||
github.com/kdomanski/iso9660 v0.4.0/go.mod h1:OxUSupHsO9ceI8lBLPJKWBTphLemjrCQY8LPXM7qSzU=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
@@ -777,6 +779,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mogaika/udf v0.0.0-20171019171931-167f0ab01c73 h1:HpHNB68mF30LkUorpr7B22Xy0XwParP/n+AL0z4VlFc=
|
||||
github.com/mogaika/udf v0.0.0-20171019171931-167f0ab01c73/go.mod h1:OVErfG87tRGCHZpQGO8pL4vDZuY0qr4blPOTR0go6e8=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
@@ -1165,8 +1169,8 @@ golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8/go.mod h1:IxCIyHEi3zRg3s0
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -1268,8 +1272,8 @@ golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -1292,8 +1296,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -1381,16 +1385,16 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1405,8 +1409,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -1676,8 +1680,8 @@ yunion.io/x/ovsdb v0.0.0-20230306173834-f164f413a900 h1:Hu/4ERvoWaN6aiFs4h4/yvVB
|
||||
yunion.io/x/ovsdb v0.0.0-20230306173834-f164f413a900/go.mod h1:0vLkNEhlmA64HViPBAnSTUMrx5QP1CLsxXmxDKQ80tc=
|
||||
yunion.io/x/pkg v0.0.0-20190620104149-945c25821dbf/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
|
||||
yunion.io/x/pkg v0.0.0-20190628082551-f4033ba2ea30/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
|
||||
yunion.io/x/pkg v1.10.4-0.20251114095758-2a2f105d9712 h1:LUYhaE2PbRrlO2dJTII3K5L62wYlwDet2drziGv6yLY=
|
||||
yunion.io/x/pkg v1.10.4-0.20251114095758-2a2f105d9712/go.mod h1:0Bwxqd9MA3ACi119/l02FprY/o9gHahmYC2bsSbnVpM=
|
||||
yunion.io/x/pkg v1.10.4-0.20260127060125-8939521ef75e h1:py5Kd6cgP6pxB9pSc4ry/gnmA6rJZwMVOnAjra4sb2g=
|
||||
yunion.io/x/pkg v1.10.4-0.20260127060125-8939521ef75e/go.mod h1:0Bwxqd9MA3ACi119/l02FprY/o9gHahmYC2bsSbnVpM=
|
||||
yunion.io/x/s3cli v0.0.0-20241221171442-1c11599d28e1 h1:1KJ3YYinydPHpDEQRXdr/T8SYcKZ5Er+m489H+PnaQ4=
|
||||
yunion.io/x/s3cli v0.0.0-20241221171442-1c11599d28e1/go.mod h1:0iFKpOs1y4lbCxeOmq3Xx/0AcQoewVPwj62eRluioEo=
|
||||
yunion.io/x/sqlchemy v1.1.3-0.20251231025938-b0a38f6e9fab h1:5m/bSzW3uTAk83rp9eethbYsxJFYInFVeU1RDkteW4E=
|
||||
|
||||
@@ -19,13 +19,14 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apigateway/clientman"
|
||||
"yunion.io/x/onecloud/pkg/appsrv"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/util/netutils2"
|
||||
"yunion.io/x/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -60,6 +60,7 @@ const (
|
||||
IMAGE_OS_DISTRO = "os_distribution"
|
||||
IMAGE_OS_TYPE = "os_type"
|
||||
IMAGE_OS_VERSION = "os_version"
|
||||
IMAGE_OS_LANGUAGE = "os_language"
|
||||
IMAGE_DISK_FORMAT = "disk_format"
|
||||
IMAGE_UEFI_SUPPORT = "uefi_support"
|
||||
IMAGE_BIOS_SUPPORT = "bios_support"
|
||||
|
||||
@@ -24,9 +24,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/coredns/coredns/plugin/pkg/log"
|
||||
"yunion.io/x/cloudmux/pkg/cloudprovider"
|
||||
"yunion.io/x/cloudmux/pkg/multicloud/objectstore"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/streamutils"
|
||||
)
|
||||
|
||||
@@ -63,6 +63,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules/notify"
|
||||
"yunion.io/x/onecloud/pkg/util/cephutils"
|
||||
"yunion.io/x/onecloud/pkg/util/fileutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/isoutils"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
"yunion.io/x/onecloud/pkg/util/procutils"
|
||||
"yunion.io/x/onecloud/pkg/util/qemuimg"
|
||||
@@ -1734,8 +1735,24 @@ func (m *SImageManager) PerformVmwareAccountAdded(ctx context.Context, userCred
|
||||
|
||||
func (image *SImage) doProbeImageInfo(ctx context.Context, userCred mcclient.TokenCredential) (bool, error) {
|
||||
if image.IsIso() {
|
||||
// no need to probe
|
||||
return false, nil
|
||||
imagePath := image.GetLocalLocation()
|
||||
if len(imagePath) == 0 {
|
||||
return false, errors.Wrapf(httperrors.ErrNotFound, "image file %s not found", image.Location)
|
||||
}
|
||||
fp, err := os.Open(imagePath)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "Open image file")
|
||||
}
|
||||
defer fp.Close()
|
||||
isoInfo, err := isoutils.DetectOSFromISO(fp)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "DetectOSFromISO")
|
||||
}
|
||||
err = image.updateIsoInfo(ctx, userCred, isoInfo)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "updateIsoInfo")
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
if image.IsData.IsTrue() {
|
||||
// no need to probe
|
||||
@@ -1789,9 +1806,9 @@ func (image *SImage) updateImageInfo(
|
||||
imageProperties := jsonutils.Marshal(imageInfo.OsInfo).(*jsonutils.JSONDict)
|
||||
|
||||
imageProperties.Set(api.IMAGE_OS_ARCH, jsonutils.NewString(imageInfo.OsInfo.Arch))
|
||||
imageProperties.Set("os_version", jsonutils.NewString(imageInfo.OsInfo.Version))
|
||||
imageProperties.Set("os_distribution", jsonutils.NewString(imageInfo.OsInfo.Distro))
|
||||
imageProperties.Set("os_language", jsonutils.NewString(imageInfo.OsInfo.Language))
|
||||
imageProperties.Set(api.IMAGE_OS_VERSION, jsonutils.NewString(imageInfo.OsInfo.Version))
|
||||
imageProperties.Set(api.IMAGE_OS_DISTRO, jsonutils.NewString(imageInfo.OsInfo.Distro))
|
||||
imageProperties.Set(api.IMAGE_OS_LANGUAGE, jsonutils.NewString(imageInfo.OsInfo.Language))
|
||||
|
||||
imageProperties.Set(api.IMAGE_OS_TYPE, jsonutils.NewString(imageInfo.OsType))
|
||||
imageProperties.Set(api.IMAGE_PARTITION_TYPE, jsonutils.NewString(imageInfo.PhysicalPartitionType))
|
||||
@@ -1804,6 +1821,38 @@ func (image *SImage) updateImageInfo(
|
||||
return ImagePropertyManager.SaveProperties(ctx, userCred, image.Id, imageProperties)
|
||||
}
|
||||
|
||||
func (image *SImage) updateIsoInfo(ctx context.Context, userCred mcclient.TokenCredential, imageInfo *isoutils.ISOInfo) error {
|
||||
if gotypes.IsNil(imageInfo) || len(imageInfo.Distro) == 0 {
|
||||
return nil
|
||||
}
|
||||
change := false
|
||||
imageProperties := jsonutils.Marshal(imageInfo).(*jsonutils.JSONDict)
|
||||
if len(imageInfo.Arch) > 0 {
|
||||
imageProperties.Set(api.IMAGE_OS_ARCH, jsonutils.NewString(imageInfo.Arch))
|
||||
change = true
|
||||
db.Update(image, func() error {
|
||||
image.OsArch = imageInfo.Arch
|
||||
return nil
|
||||
})
|
||||
}
|
||||
if len(imageInfo.Version) > 0 {
|
||||
imageProperties.Set(api.IMAGE_OS_VERSION, jsonutils.NewString(imageInfo.Version))
|
||||
change = true
|
||||
}
|
||||
if len(imageInfo.Distro) > 0 {
|
||||
imageProperties.Set(api.IMAGE_OS_DISTRO, jsonutils.NewString(imageInfo.Distro))
|
||||
change = true
|
||||
}
|
||||
if len(imageInfo.Language) > 0 {
|
||||
imageProperties.Set(api.IMAGE_OS_LANGUAGE, jsonutils.NewString(imageInfo.Language))
|
||||
change = true
|
||||
}
|
||||
if change {
|
||||
return ImagePropertyManager.SaveProperties(ctx, userCred, image.Id, imageProperties)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (image *SImage) updateChecksum() error {
|
||||
imagePath := image.GetLocalLocation()
|
||||
if len(imagePath) == 0 {
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/llm"
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/llm"
|
||||
|
||||
@@ -18,9 +18,11 @@ import (
|
||||
commonapis "yunion.io/x/onecloud/pkg/apis"
|
||||
computeapi "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
imageapi "yunion.io/x/onecloud/pkg/apis/image"
|
||||
apis "yunion.io/x/onecloud/pkg/apis/llm"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/llm/options"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
computemodules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
|
||||
@@ -29,9 +31,6 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
"yunion.io/x/onecloud/pkg/util/procutils"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
|
||||
apis "yunion.io/x/onecloud/pkg/apis/llm"
|
||||
"yunion.io/x/onecloud/pkg/llm/options"
|
||||
)
|
||||
|
||||
var instantModelManager *SInstantModelManager
|
||||
|
||||
@@ -7,10 +7,9 @@ import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
apis "yunion.io/x/onecloud/pkg/apis/llm"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
|
||||
apis "yunion.io/x/onecloud/pkg/apis/llm"
|
||||
)
|
||||
|
||||
type SMountedModelsResourceManager struct {
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/log"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/identity"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/policy"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/identity"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcp-server/adapters"
|
||||
"yunion.io/x/onecloud/pkg/mcp-server/options"
|
||||
"yunion.io/x/onecloud/pkg/mcp-server/registry"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
package isoutils // import "yunion.io/x/onecloud/pkg/util/isoutils"
|
||||
@@ -0,0 +1,148 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package isoutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/kdomanski/iso9660"
|
||||
|
||||
"yunion.io/x/log"
|
||||
)
|
||||
|
||||
// findISO9660File 在ISO9660中查找文件,返回 *iso9660.File
|
||||
func (r *ISOFileReader) findISO9660File(path string) (*iso9660.File, error) {
|
||||
if r.format != ISOFormatISO9660 || r.iso9660Img == nil {
|
||||
return nil, fmt.Errorf("ISO9660格式未初始化")
|
||||
}
|
||||
|
||||
rootDir, err := r.iso9660Img.RootDir()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取根目录失败: %v", err)
|
||||
}
|
||||
|
||||
// 规范化路径
|
||||
path = strings.Trim(path, "/")
|
||||
if path == "" {
|
||||
return rootDir, nil
|
||||
}
|
||||
|
||||
parts := strings.Split(path, "/")
|
||||
currentDir := rootDir
|
||||
|
||||
for i, part := range parts {
|
||||
if part == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// 获取当前目录的子项
|
||||
children, err := currentDir.GetChildren()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取目录失败: %v", err)
|
||||
}
|
||||
|
||||
// 查找匹配的文件或目录(不区分大小写)
|
||||
var found *iso9660.File
|
||||
for _, child := range children {
|
||||
if strings.EqualFold(child.Name(), part) {
|
||||
found = child
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if found == nil {
|
||||
return nil, fmt.Errorf("文件或目录不存在: %s", part)
|
||||
}
|
||||
|
||||
// 如果是最后一个部分,返回找到的文件
|
||||
if i == len(parts)-1 {
|
||||
return found, nil
|
||||
}
|
||||
|
||||
// 检查是否为目录
|
||||
if !found.IsDir() {
|
||||
return nil, fmt.Errorf("路径中的%s不是目录", part)
|
||||
}
|
||||
|
||||
currentDir = found
|
||||
}
|
||||
|
||||
return currentDir, nil
|
||||
}
|
||||
|
||||
// listISO9660Dir 列出ISO9660格式指定目录下的所有文件和子目录
|
||||
func (r *ISOFileReader) listISO9660Dir(path string) ([]ISO9660FileInfo, error) {
|
||||
if r.format != ISOFormatISO9660 {
|
||||
return nil, fmt.Errorf("此方法仅支持ISO9660格式")
|
||||
}
|
||||
|
||||
// 获取目录
|
||||
dir, err := r.findISO9660File(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !dir.IsDir() {
|
||||
return nil, fmt.Errorf("路径%s不是目录", path)
|
||||
}
|
||||
|
||||
// 获取子项
|
||||
children, err := dir.GetChildren()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取目录失败: %v", err)
|
||||
}
|
||||
|
||||
var files []ISO9660FileInfo
|
||||
for _, child := range children {
|
||||
fileInfo := ISO9660FileInfo{
|
||||
Name: child.Name(),
|
||||
IsDir: child.IsDir(),
|
||||
Size: child.Size(),
|
||||
Location: 0, // 使用库时不需要直接访问位置
|
||||
}
|
||||
files = append(files, fileInfo)
|
||||
}
|
||||
|
||||
return files, nil
|
||||
}
|
||||
|
||||
// readISO9660FileContent 读取ISO9660格式文件内容
|
||||
func (r *ISOFileReader) readISO9660FileContent(path string) (string, error) {
|
||||
file, err := r.findISO9660File(path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("文件%s不存在: %v", path, err)
|
||||
}
|
||||
|
||||
if file.IsDir() {
|
||||
return "", fmt.Errorf("路径%s是目录,不是文件", path)
|
||||
}
|
||||
|
||||
reader := file.Reader()
|
||||
if reader == nil {
|
||||
return "", fmt.Errorf("无法读取文件%s", path)
|
||||
}
|
||||
|
||||
// 读取前10KB内容(足够识别特征)
|
||||
buf := make([]byte, 10*1024)
|
||||
n, err := reader.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
log.Errorf("读取ISO9660文件%s失败: %v", path, err)
|
||||
return "", fmt.Errorf("读取文件%s失败: %v", path, err)
|
||||
}
|
||||
|
||||
return strings.TrimSpace(string(buf[:n])), nil
|
||||
}
|
||||
@@ -0,0 +1,428 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package isoutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/kdomanski/iso9660"
|
||||
"github.com/mogaika/udf"
|
||||
"gopkg.in/ini.v1"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/util/imagetools"
|
||||
)
|
||||
|
||||
// ========== 2. 新增结构化返回结果(包含发行版、版本号、架构) ==========
|
||||
type ISOInfo struct {
|
||||
Distro string // 发行版(如 CentOS、Ubuntu Server)
|
||||
Version string // 版本号(如 7.9、22.04 LTS、2022)
|
||||
Arch string // 架构(如 x86_64、riscv64、arm64)
|
||||
Language string // 语言(如 en-US、zh-CN)
|
||||
}
|
||||
|
||||
// ISO格式类型
|
||||
type ISOFormat string
|
||||
|
||||
const (
|
||||
ISOFormatUnknown ISOFormat = "unknown"
|
||||
ISOFormatUDF ISOFormat = "udf"
|
||||
ISOFormatISO9660 ISOFormat = "iso9660"
|
||||
)
|
||||
|
||||
// ========== 3. 优化ISOFileReader:增加缓存、日志、架构识别 ==========
|
||||
type ISOFileReader struct {
|
||||
format ISOFormat
|
||||
img *udf.Udf
|
||||
iso9660Img *iso9660.Image // ISO9660格式的读取器
|
||||
reader io.Reader
|
||||
cache sync.Map // 缓存已读取的文件内容:key=文件路径,value=文件内容
|
||||
}
|
||||
|
||||
// isIsoFile 检测ISO格式(UDF或ISO9660)
|
||||
func isIsoFile(readerAt io.ReaderAt) (bool, error) {
|
||||
// 读取0x8000地址的内容(ISO9660的Primary Volume Descriptor位置)
|
||||
buf := make([]byte, 6)
|
||||
n, err := readerAt.ReadAt(buf, 0x8000)
|
||||
if err != nil && err != io.EOF {
|
||||
return false, fmt.Errorf("读取ISO格式标识失败: %v", err)
|
||||
}
|
||||
|
||||
if n < 6 {
|
||||
return false, fmt.Errorf("读取数据不足")
|
||||
}
|
||||
|
||||
// ISO9660格式:偏移0x8001-0x8005应该是"CD001"
|
||||
if bytes.Equal(buf[1:6], []byte("CD001")) {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// NewISOFileReader 初始化ISO读取器(新增格式检测和日志配置)
|
||||
func NewISOFileReader(reader io.Reader) (*ISOFileReader, error) {
|
||||
readerAt, ok := reader.(io.ReaderAt)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("reader is not io.ReaderAt")
|
||||
}
|
||||
|
||||
// 检测ISO格式
|
||||
isIso, err := isIsoFile(readerAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !isIso {
|
||||
return nil, fmt.Errorf("ISO镜像格式不正确")
|
||||
}
|
||||
|
||||
ret := &ISOFileReader{
|
||||
format: ISOFormatISO9660,
|
||||
reader: reader,
|
||||
cache: sync.Map{},
|
||||
}
|
||||
|
||||
if isUdfFile(readerAt) {
|
||||
ret.format = ISOFormatUDF
|
||||
ret.img = udf.NewUdfFromReader(readerAt)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
isoImg, err := iso9660.OpenImage(readerAt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("打开ISO9660镜像失败: %v", err)
|
||||
}
|
||||
ret.iso9660Img = isoImg
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// ISO9660FileInfo ISO9660文件信息
|
||||
type ISO9660FileInfo struct {
|
||||
Name string // 文件名
|
||||
IsDir bool // 是否为目录
|
||||
Size int64 // 文件大小(字节)
|
||||
Location int64 // 文件在ISO中的位置(字节偏移,使用库时可能为0)
|
||||
}
|
||||
|
||||
func (r *ISOFileReader) list(path string) ([]ISO9660FileInfo, error) {
|
||||
if r.format == ISOFormatISO9660 {
|
||||
return r.listISO9660Dir(path)
|
||||
}
|
||||
return r.listUdfDir(path)
|
||||
}
|
||||
|
||||
// FileExists 检查ISO内指定路径的文件是否存在(支持UDF和ISO9660)
|
||||
func (r *ISOFileReader) FileExists(path string) bool {
|
||||
if r.format == ISOFormatISO9660 {
|
||||
_, err := r.findISO9660File(path)
|
||||
return err == nil
|
||||
}
|
||||
_, err := r.GetFile(path)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// ReadFileContent 读取ISO内指定文件的内容(新增缓存、日志,支持UDF和ISO9660)
|
||||
func (r *ISOFileReader) ReadFileContent(path string) (string, error) {
|
||||
// 优先从缓存读取
|
||||
if cacheVal, ok := r.cache.Load(path); ok {
|
||||
log.Debugf("从缓存读取文件内容: %s", path)
|
||||
return cacheVal.(string), nil
|
||||
}
|
||||
|
||||
var content string
|
||||
var err error
|
||||
|
||||
// 根据格式选择相应的读取方法
|
||||
if r.format == ISOFormatISO9660 {
|
||||
content, err = r.readISO9660FileContent(path)
|
||||
} else if r.format == ISOFormatUDF {
|
||||
content, err = r.readUdfFileContent(path)
|
||||
} else {
|
||||
return "", fmt.Errorf("未知的ISO格式: %s", r.format)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 写入缓存
|
||||
r.cache.Store(path, content)
|
||||
log.Debugf("读取文件%s内容(长度: %d)并缓存", path, len(content))
|
||||
|
||||
return content, nil
|
||||
}
|
||||
|
||||
// ========== 6. 核心识别函数:整合版本号、架构、日志、缓存 ==========
|
||||
func DetectOSFromISO(r io.Reader) (*ISOInfo, error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Errorf("DetectOSFromISO panic error: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
result := &ISOInfo{}
|
||||
|
||||
// 初始化读取器
|
||||
reader, err := NewISOFileReader(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ========== 识别Windows系列 ==========
|
||||
if reader.FileExists("sources/install.wim") {
|
||||
return DetectWindowsEdition(reader)
|
||||
}
|
||||
|
||||
files, err := reader.list("/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, file := range files {
|
||||
fileName := file.Name
|
||||
|
||||
if fileName == ".treeinfo" {
|
||||
content, _ := reader.ReadFileContent(fileName)
|
||||
result = getOsInfoByIniFile(content)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if reader.FileExists(".disk/info") {
|
||||
content, _ := reader.ReadFileContent(".disk/info")
|
||||
info := imagetools.NormalizeImageInfo(content, "", "", "", "")
|
||||
result = &ISOInfo{
|
||||
Distro: info.OsDistro,
|
||||
Version: info.OsVersion,
|
||||
Arch: info.OsArch,
|
||||
Language: info.OsLang,
|
||||
}
|
||||
}
|
||||
|
||||
if len(result.Distro) == 0 || result.Distro == imagetools.OS_DIST_OTHER_LINUX {
|
||||
realeaseFile := ""
|
||||
if reader.FileExists("dists") {
|
||||
files, err := reader.list("dists")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
log.Debugf("file: %s", file.Name)
|
||||
if !file.IsDir {
|
||||
continue
|
||||
}
|
||||
subFiles, err := reader.list(fmt.Sprintf("dists/%s", file.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, subFile := range subFiles {
|
||||
if subFile.Name == "Release" {
|
||||
realeaseFile = fmt.Sprintf("dists/%s/%s", file.Name, subFile.Name)
|
||||
break
|
||||
}
|
||||
}
|
||||
if realeaseFile != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if realeaseFile != "" {
|
||||
content, _ := reader.ReadFileContent(realeaseFile)
|
||||
result = getOsInfoByReleaseFile(content)
|
||||
} else if reader.FileExists("boot/grub2/grub.cfg") {
|
||||
content, _ := reader.ReadFileContent("boot/grub2/grub.cfg")
|
||||
result = getOsInfoByGrub(content)
|
||||
} else if reader.FileExists("EFI/BOOT/grub.cfg") {
|
||||
content, _ := reader.ReadFileContent("EFI/BOOT/grub.cfg")
|
||||
result = getOsInfoByGrub(content)
|
||||
} else if reader.FileExists("isolinux/isolinux.cfg") {
|
||||
content, _ := reader.ReadFileContent("isolinux/isolinux.cfg")
|
||||
result = getOsInfoByIsoLinux(content)
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func getOsInfoByReleaseFile(content string) *ISOInfo {
|
||||
result := &ISOInfo{}
|
||||
for _, line := range strings.Split(content, "\n") {
|
||||
if strings.HasPrefix(line, "Origin:") {
|
||||
result.Distro = strings.TrimSpace(strings.TrimPrefix(line, "Origin:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Label:") && len(result.Distro) == 0 {
|
||||
result.Distro = strings.TrimSpace(strings.TrimPrefix(line, "Label:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Version:") {
|
||||
result.Version = strings.TrimSpace(strings.TrimPrefix(line, "Version:"))
|
||||
}
|
||||
if strings.HasPrefix(line, "Architectures:") {
|
||||
result.Arch = strings.TrimSpace(strings.TrimPrefix(line, "Architectures:"))
|
||||
result.Arch = detectArchitecture(strings.ToLower(result.Arch), result.Arch)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func getOsInfoByIniFile(content string) *ISOInfo {
|
||||
cfg, err := ini.Load([]byte(content))
|
||||
if err != nil {
|
||||
// 兼容手动解析(应对部分非标准 INI 格式的 .treeinfo)
|
||||
return parseTreeInfoFallback(content)
|
||||
}
|
||||
release := cfg.Section("release")
|
||||
ret := &ISOInfo{}
|
||||
ret.Distro = release.Key("name").String()
|
||||
ret.Version = release.Key("version").String()
|
||||
general := cfg.Section("general")
|
||||
ret.Arch = general.Key("arch").String()
|
||||
if len(ret.Version) == 0 {
|
||||
ret.Version = general.Key("version").String()
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func parseTreeInfoFallback(content string) *ISOInfo {
|
||||
result := &ISOInfo{}
|
||||
info := strings.Split(content, "\n")
|
||||
for _, line := range info {
|
||||
if strings.HasPrefix(line, "arch =") {
|
||||
result.Arch = strings.TrimPrefix(line, "arch = ")
|
||||
}
|
||||
if strings.HasPrefix(line, "version =") {
|
||||
result.Version = strings.TrimPrefix(line, "version = ")
|
||||
}
|
||||
if strings.HasPrefix(line, "name =") {
|
||||
result.Distro = strings.TrimPrefix(line, "name = ")
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func getOsInfoByIsoLinux(content string) *ISOInfo {
|
||||
result := &ISOInfo{}
|
||||
lowerContent := strings.ToLower(content)
|
||||
// 5.1 识别发行版(关键词匹配)
|
||||
result.Distro = detectDistro(lowerContent)
|
||||
|
||||
// 5.2 识别版本号(正则提取)
|
||||
result.Version = detectVersion(content)
|
||||
|
||||
// 5.3 识别 CPU 架构(关键词+正则)
|
||||
result.Arch = detectArchitecture(lowerContent, content)
|
||||
return result
|
||||
}
|
||||
|
||||
func getOsInfoByGrub(content string) *ISOInfo {
|
||||
result := &ISOInfo{}
|
||||
lowerContent := strings.ToLower(content)
|
||||
result.Distro = detectDistro(lowerContent)
|
||||
result.Version = detectGrubVersion(content)
|
||||
result.Arch = detectArchitecture(lowerContent, content)
|
||||
return result
|
||||
}
|
||||
|
||||
func detectDistro(lowerContent string) string {
|
||||
info := imagetools.NormalizeImageInfo(lowerContent, "", "", "", "")
|
||||
return info.OsDistro
|
||||
}
|
||||
|
||||
// detectVersion 从配置内容中提取版本号
|
||||
func detectVersion(content string) string {
|
||||
// 匹配版本号的正则(支持 x x.y、x.y.z、x.y-LTS 等格式)
|
||||
versionRegex := regexp.MustCompile(`(\d+(\.\d+(\.\d+)?)?(-[A-Za-z0-9]+)?)`)
|
||||
|
||||
// 优先从启动标题(label/menu label)中提取
|
||||
labelLines := regexp.MustCompile(`(?i)menu label .+Install.+`).FindAllStringSubmatch(content, -1)
|
||||
for _, line := range labelLines {
|
||||
log.Debugf("line: %s", line)
|
||||
if len(line) >= 1 {
|
||||
version := versionRegex.FindString(line[0])
|
||||
if version != "" {
|
||||
return version
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 从整个内容中提取第一个匹配的版本号
|
||||
return versionRegex.FindString(content)
|
||||
}
|
||||
|
||||
// detectArchitecture 识别 CPU 架构
|
||||
func detectArchitecture(lowerContent, rawContent string) string {
|
||||
// 架构关键词映射
|
||||
archKeywords := map[string][]string{
|
||||
"x86_64": {"x86_64", "amd64"},
|
||||
"aarch64": {"aarch64", "arm64"},
|
||||
"i386": {"i386", "i686"},
|
||||
"armhfp": {"armhfp", "armv7"},
|
||||
"ppc64le": {"ppc64le"},
|
||||
"s390x": {"s390x"},
|
||||
}
|
||||
|
||||
for arch, keywords := range archKeywords {
|
||||
for _, kw := range keywords {
|
||||
if strings.Contains(lowerContent, kw) {
|
||||
return arch
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 从内核文件名(vmlinuz/initrd)中提取
|
||||
kernelRegex := regexp.MustCompile(`vmlinuz-([a-zA-Z0-9_]+)`)
|
||||
match := kernelRegex.FindStringSubmatch(rawContent)
|
||||
if len(match) >= 2 {
|
||||
return match[1]
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// detectGrubVersion 从 grub.cfg 提取版本号
|
||||
func detectGrubVersion(content string) string {
|
||||
// 匹配版本号的正则(支持 x x.y、x.y.z、x.y-LTS、x.y.z-xxx 等格式)
|
||||
versionRegex := regexp.MustCompile(`(\d+(\.\d+(\.\d+)?)?(-[A-Za-z0-9]+)?)`)
|
||||
|
||||
// 优先从 GRUB 菜单标题(menuentry)中提取(准确性更高)
|
||||
menuEntryRegex := regexp.MustCompile(`(?i)menuentry\s+["'](.+?)["']`)
|
||||
menuEntries := menuEntryRegex.FindAllStringSubmatch(content, -1)
|
||||
for _, entry := range menuEntries {
|
||||
log.Debugf("entry: %s", entry)
|
||||
if len(entry) >= 1 {
|
||||
version := versionRegex.FindString(entry[0])
|
||||
if version != "" {
|
||||
return version
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 从内核文件名/参数中提取
|
||||
kernelLines := regexp.MustCompile(`linux\s+.+`).FindAllString(content, -1)
|
||||
for _, line := range kernelLines {
|
||||
version := versionRegex.FindString(line)
|
||||
if version != "" {
|
||||
return version
|
||||
}
|
||||
}
|
||||
|
||||
// 最后从整个内容中提取第一个匹配的版本号
|
||||
return versionRegex.FindString(content)
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package isoutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/mogaika/udf"
|
||||
|
||||
"yunion.io/x/log"
|
||||
)
|
||||
|
||||
// isUdfFile 检测是否为UDF格式
|
||||
func isUdfFile(readerAt io.ReaderAt) bool {
|
||||
defer func() {
|
||||
recover()
|
||||
}()
|
||||
|
||||
img := udf.NewUdfFromReader(readerAt)
|
||||
files := img.ReadDir(nil)
|
||||
if len(files) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// findUdfDir 在UDF中查找目录,返回目录的 FileEntry
|
||||
func (r *ISOFileReader) findUdfDir(path string) ([]udf.File, error) {
|
||||
if r.format != ISOFormatUDF || r.img == nil {
|
||||
return nil, fmt.Errorf("UDF格式未初始化")
|
||||
}
|
||||
|
||||
// 规范化路径
|
||||
path = strings.Trim(path, "/")
|
||||
if path == "" {
|
||||
// 根目录
|
||||
return r.img.ReadDir(nil), nil
|
||||
}
|
||||
|
||||
// 查找目录路径
|
||||
parts := strings.Split(path, "/")
|
||||
var entry *udf.FileEntry = nil
|
||||
currentDirEntry := r.img.ReadDir(entry) // 从根目录开始
|
||||
|
||||
for i, part := range parts {
|
||||
if part == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
var found *udf.File
|
||||
// 在当前目录中查找
|
||||
for idx := range currentDirEntry {
|
||||
child := ¤tDirEntry[idx]
|
||||
childName := child.Name()
|
||||
// 匹配文件名(不区分大小写)
|
||||
if strings.EqualFold(childName, part) {
|
||||
found = child
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if found == nil {
|
||||
return nil, fmt.Errorf("目录不存在: %s", part)
|
||||
}
|
||||
|
||||
// 如果是最后一个部分,返回该目录的内容
|
||||
if i == len(parts)-1 {
|
||||
return found.ReadDir(), nil
|
||||
}
|
||||
|
||||
// 继续查找下一级目录
|
||||
currentDirEntry = found.ReadDir()
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("未找到目录: %s", path)
|
||||
}
|
||||
|
||||
// listUdfDir 列出UDF格式指定目录下的所有文件和子目录
|
||||
func (r *ISOFileReader) listUdfDir(path string) ([]ISO9660FileInfo, error) {
|
||||
if r.format != ISOFormatUDF {
|
||||
return nil, fmt.Errorf("此方法仅支持UDF格式")
|
||||
}
|
||||
|
||||
// 获取目录内容
|
||||
children, err := r.findUdfDir(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var files []ISO9660FileInfo
|
||||
for _, child := range children {
|
||||
fileInfo := ISO9660FileInfo{
|
||||
Name: child.Name(),
|
||||
IsDir: child.IsDir(),
|
||||
Size: child.Size(),
|
||||
Location: 0, // 使用库时不需要直接访问位置
|
||||
}
|
||||
files = append(files, fileInfo)
|
||||
}
|
||||
|
||||
return files, nil
|
||||
}
|
||||
|
||||
// GetFile 在UDF中查找指定路径的文件
|
||||
func (r *ISOFileReader) GetFile(path string) (*udf.File, error) {
|
||||
if r.format != ISOFormatUDF {
|
||||
return nil, fmt.Errorf("此方法仅支持UDF格式")
|
||||
}
|
||||
|
||||
// UDF格式的原有逻辑
|
||||
parts := strings.Split(strings.Trim(path, "/"), "/")
|
||||
|
||||
var entry *udf.FileEntry = nil
|
||||
currentDirEntry := r.img.ReadDir(entry) // 从根目录开始
|
||||
|
||||
for i, part := range parts {
|
||||
if part == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
var found *udf.File
|
||||
// 在当前目录中查找
|
||||
for idx := range currentDirEntry {
|
||||
child := ¤tDirEntry[idx]
|
||||
childName := child.Name()
|
||||
// 匹配文件名(UDF 文件名通常不包含版本号后缀)
|
||||
if childName == part {
|
||||
found = child
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if found == nil {
|
||||
return nil, fmt.Errorf("文件或目录不存在: %s", part)
|
||||
}
|
||||
|
||||
// 如果是最后一个部分,返回文件
|
||||
if i == len(parts)-1 {
|
||||
return found, nil
|
||||
}
|
||||
|
||||
currentDirEntry = found.ReadDir()
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("未找到文件: %s", path)
|
||||
}
|
||||
|
||||
// readUdfFileContent 读取UDF格式文件内容
|
||||
func (r *ISOFileReader) readUdfFileContent(path string) (string, error) {
|
||||
file, err := r.GetFile(path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("文件%s不存在: %v", path, err)
|
||||
}
|
||||
|
||||
if file.IsDir() {
|
||||
return "", fmt.Errorf("路径%s是目录,不是文件", path)
|
||||
}
|
||||
|
||||
reader := file.NewReader()
|
||||
if reader == nil {
|
||||
return "", fmt.Errorf("无法读取文件%s", path)
|
||||
}
|
||||
|
||||
// 读取前10KB内容(足够识别特征)
|
||||
buf := make([]byte, 10*1024)
|
||||
n, err := reader.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
log.Errorf("读取UDF文件%s失败: %v", path, err)
|
||||
return "", fmt.Errorf("读取文件%s失败: %v", path, err)
|
||||
}
|
||||
|
||||
return strings.TrimSpace(string(buf[:n])), nil
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package isoutils
|
||||
|
||||
import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
)
|
||||
|
||||
func DetectWindowsEdition(r *ISOFileReader) (*ISOInfo, error) {
|
||||
return nil, errors.Wrap(errors.ErrNotSupported, "not supported")
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package isoutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Microsoft/go-winio/wim"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/util/imagetools"
|
||||
)
|
||||
|
||||
// ========== 7. 保留Windows版本识别函数(适配新结构) ==========
|
||||
func DetectWindowsEdition(r *ISOFileReader) (*ISOInfo, error) {
|
||||
wimFile, err := r.GetFile("sources/install.wim")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
wim, err := wim.NewReader(wimFile.NewReader())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := &ISOInfo{}
|
||||
for _, image := range wim.Image {
|
||||
version := fmt.Sprintf("%d.%d.%d", image.Windows.Version.Major, image.Windows.Version.Minor, image.Windows.Version.Build)
|
||||
if image.Windows != nil {
|
||||
if image.Windows.Arch == 9 {
|
||||
result.Arch = "x86_64"
|
||||
} else if image.Windows.Arch == 12 {
|
||||
result.Arch = "arm64"
|
||||
} else if image.Windows.Arch == 0 {
|
||||
result.Arch = "x86"
|
||||
}
|
||||
result.Distro = imagetools.OS_DIST_WINDOWS
|
||||
result.Language = image.Windows.DefaultLanguage
|
||||
switch fmt.Sprintf("%d.%d", image.Windows.Version.Major, image.Windows.Version.Minor) {
|
||||
case "6.0":
|
||||
result.Version = "Windows Vista"
|
||||
case "6.1":
|
||||
result.Version = "Windows 7"
|
||||
case "6.2":
|
||||
result.Version = "Windows 8"
|
||||
case "6.3":
|
||||
result.Version = "Windows 8.1"
|
||||
case "10.0":
|
||||
if image.Windows.Version.Build >= 27500 {
|
||||
result.Version = "Windows 12"
|
||||
} else if image.Windows.Version.Build >= 22000 {
|
||||
result.Version = "Windows 11"
|
||||
} else {
|
||||
result.Version = "Windows 10"
|
||||
}
|
||||
}
|
||||
if image.Windows.ProductType == "ServerNT" {
|
||||
result.Distro = imagetools.OS_DIST_WINDOWS_SERVER
|
||||
switch fmt.Sprintf("%d.%d", image.Windows.Version.Major, image.Windows.Version.Minor) {
|
||||
case "6.0":
|
||||
result.Version = "Windows Server 2008"
|
||||
case "6.1":
|
||||
result.Version = "Windows Server 2008 R2"
|
||||
case "6.2":
|
||||
result.Version = "Windows Server 2012"
|
||||
case "6.3":
|
||||
result.Version = "Windows Server 2012 R2"
|
||||
case "10.0":
|
||||
if image.Windows.Version.Build >= 26040 {
|
||||
result.Version = "Windows Server 2025"
|
||||
} else if image.Windows.Version.Build >= 20348 {
|
||||
result.Version = "Windows Server 2022"
|
||||
} else if image.Windows.Version.Build >= 17763 {
|
||||
result.Version = "Windows Server 2019"
|
||||
} else if image.Windows.Version.Build >= 14393 {
|
||||
result.Version = "Windows Server 2016"
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Debugf("识别到 %s 版本: %s -> %s", result.Distro, version, result.Version)
|
||||
break
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
//go:build windows || linux
|
||||
// +build windows linux
|
||||
|
||||
package wim
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/Microsoft/go-winio/wim/lzx"
|
||||
)
|
||||
|
||||
const chunkSize = 32768 // Compressed resource chunk size
|
||||
|
||||
type compressedReader struct {
|
||||
r *io.SectionReader
|
||||
d io.ReadCloser
|
||||
chunks []int64
|
||||
curChunk int
|
||||
originalSize int64
|
||||
}
|
||||
|
||||
func newCompressedReader(r *io.SectionReader, originalSize int64, offset int64) (*compressedReader, error) {
|
||||
nchunks := (originalSize + chunkSize - 1) / chunkSize
|
||||
var base int64
|
||||
chunks := make([]int64, nchunks)
|
||||
if originalSize <= 0xffffffff {
|
||||
// 32-bit chunk offsets
|
||||
base = (nchunks - 1) * 4
|
||||
chunks32 := make([]uint32, nchunks-1)
|
||||
err := binary.Read(r, binary.LittleEndian, chunks32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i, n := range chunks32 {
|
||||
chunks[i+1] = int64(n)
|
||||
}
|
||||
} else {
|
||||
// 64-bit chunk offsets
|
||||
base = (nchunks - 1) * 8
|
||||
err := binary.Read(r, binary.LittleEndian, chunks[1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for i, c := range chunks {
|
||||
chunks[i] = c + base
|
||||
}
|
||||
|
||||
cr := &compressedReader{
|
||||
r: r,
|
||||
chunks: chunks,
|
||||
originalSize: originalSize,
|
||||
}
|
||||
|
||||
err := cr.reset(int(offset / chunkSize))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
suboff := offset % chunkSize
|
||||
if suboff != 0 {
|
||||
_, err := io.CopyN(io.Discard, cr.d, suboff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return cr, nil
|
||||
}
|
||||
|
||||
func (r *compressedReader) chunkOffset(n int) int64 {
|
||||
if n == len(r.chunks) {
|
||||
return r.r.Size()
|
||||
}
|
||||
return r.chunks[n]
|
||||
}
|
||||
|
||||
func (r *compressedReader) chunkSize(n int) int {
|
||||
return int(r.chunkOffset(n+1) - r.chunkOffset(n))
|
||||
}
|
||||
|
||||
func (r *compressedReader) uncompressedSize(n int) int {
|
||||
if n < len(r.chunks)-1 {
|
||||
return chunkSize
|
||||
}
|
||||
size := int(r.originalSize % chunkSize)
|
||||
if size == 0 {
|
||||
size = chunkSize
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
func (r *compressedReader) reset(n int) error {
|
||||
if n >= len(r.chunks) {
|
||||
return io.EOF
|
||||
}
|
||||
if r.d != nil {
|
||||
r.d.Close()
|
||||
}
|
||||
r.curChunk = n
|
||||
size := r.chunkSize(n)
|
||||
uncompressedSize := r.uncompressedSize(n)
|
||||
section := io.NewSectionReader(r.r, r.chunkOffset(n), int64(size))
|
||||
if size != uncompressedSize {
|
||||
d, err := lzx.NewReader(section, uncompressedSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.d = d
|
||||
} else {
|
||||
r.d = io.NopCloser(section)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *compressedReader) Read(b []byte) (int, error) {
|
||||
for {
|
||||
n, err := r.d.Read(b)
|
||||
if err != io.EOF { //nolint:errorlint
|
||||
return n, err
|
||||
}
|
||||
|
||||
err = r.reset(r.curChunk + 1)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *compressedReader) Close() error {
|
||||
var err error
|
||||
if r.d != nil {
|
||||
err = r.d.Close()
|
||||
r.d = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
+598
@@ -0,0 +1,598 @@
|
||||
// Package lzx implements a decompressor for the the WIM variant of the
|
||||
// LZX compression algorithm.
|
||||
//
|
||||
// The LZX algorithm is an earlier variant of LZX DELTA, which is documented
|
||||
// at https://msdn.microsoft.com/en-us/library/cc483133(v=exchg.80).aspx.
|
||||
package lzx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
maincodecount = 496
|
||||
maincodesplit = 256
|
||||
lencodecount = 249
|
||||
lenshift = 9
|
||||
codemask = 0x1ff
|
||||
tablebits = 9
|
||||
tablesize = 1 << tablebits
|
||||
|
||||
maxBlockSize = 32768
|
||||
windowSize = 32768
|
||||
|
||||
maxTreePathLen = 16
|
||||
|
||||
e8filesize = 12000000
|
||||
maxe8offset = 0x3fffffff
|
||||
|
||||
verbatimBlock = 1
|
||||
alignedOffsetBlock = 2
|
||||
uncompressedBlock = 3
|
||||
)
|
||||
|
||||
var footerBits = [...]byte{
|
||||
0, 0, 0, 0, 1, 1, 2, 2,
|
||||
3, 3, 4, 4, 5, 5, 6, 6,
|
||||
7, 7, 8, 8, 9, 9, 10, 10,
|
||||
11, 11, 12, 12, 13, 13, 14,
|
||||
}
|
||||
|
||||
var basePosition = [...]uint16{
|
||||
0, 1, 2, 3, 4, 6, 8, 12,
|
||||
16, 24, 32, 48, 64, 96, 128, 192,
|
||||
256, 384, 512, 768, 1024, 1536, 2048, 3072,
|
||||
4096, 6144, 8192, 12288, 16384, 24576, 32768,
|
||||
}
|
||||
|
||||
var (
|
||||
errCorrupt = errors.New("LZX data corrupt")
|
||||
)
|
||||
|
||||
// Reader is an interface used by the decompressor to access
|
||||
// the input stream. If the provided io.Reader does not implement
|
||||
// Reader, then a bufio.Reader is used.
|
||||
type Reader interface {
|
||||
io.Reader
|
||||
io.ByteReader
|
||||
}
|
||||
|
||||
type decompressor struct {
|
||||
r io.Reader
|
||||
err error
|
||||
unaligned bool
|
||||
nbits byte
|
||||
c uint32
|
||||
lru [3]uint16
|
||||
uncompressed int
|
||||
windowReader *bytes.Reader
|
||||
mainlens [maincodecount]byte
|
||||
lenlens [lencodecount]byte
|
||||
window [windowSize]byte
|
||||
b []byte
|
||||
bv int
|
||||
bo int
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func (f *decompressor) fail(err error) {
|
||||
if f.err == nil {
|
||||
f.err = err
|
||||
}
|
||||
f.bo = 0
|
||||
f.bv = 0
|
||||
}
|
||||
|
||||
func (f *decompressor) ensureAtLeast(n int) error {
|
||||
if f.bv-f.bo >= n {
|
||||
return nil
|
||||
}
|
||||
|
||||
if f.err != nil {
|
||||
return f.err
|
||||
}
|
||||
|
||||
if f.bv != f.bo {
|
||||
copy(f.b[:f.bv-f.bo], f.b[f.bo:f.bv])
|
||||
}
|
||||
n, err := io.ReadAtLeast(f.r, f.b[f.bv-f.bo:], n)
|
||||
if err != nil {
|
||||
if err == io.EOF { //nolint:errorlint
|
||||
err = io.ErrUnexpectedEOF
|
||||
} else {
|
||||
f.fail(err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
f.bv = f.bv - f.bo + n
|
||||
f.bo = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
// feed retrieves another 16-bit word from the stream and consumes
|
||||
// it into f.c. It returns false if there are no more bytes available.
|
||||
// Otherwise, on error, it sets f.err.
|
||||
func (f *decompressor) feed() bool {
|
||||
err := f.ensureAtLeast(2)
|
||||
if err == io.ErrUnexpectedEOF { //nolint:errorlint // returns io.ErrUnexpectedEOF by contract
|
||||
return false
|
||||
}
|
||||
f.c |= (uint32(f.b[f.bo+1])<<8 | uint32(f.b[f.bo])) << (16 - f.nbits)
|
||||
f.nbits += 16
|
||||
f.bo += 2
|
||||
return true
|
||||
}
|
||||
|
||||
// getBits retrieves the next n bits from the byte stream. n
|
||||
// must be <= 16. It sets f.err on error.
|
||||
func (f *decompressor) getBits(n byte) uint16 {
|
||||
if f.nbits < n {
|
||||
if !f.feed() {
|
||||
f.fail(io.ErrUnexpectedEOF)
|
||||
}
|
||||
}
|
||||
c := uint16(f.c >> (32 - n))
|
||||
f.c <<= n
|
||||
f.nbits -= n
|
||||
return c
|
||||
}
|
||||
|
||||
type huffman struct {
|
||||
extra [][]uint16
|
||||
maxbits byte
|
||||
table [tablesize]uint16
|
||||
}
|
||||
|
||||
// buildTable builds a huffman decoding table from a slice of code lengths,
|
||||
// one per code, in order. Each code length must be <= maxTreePathLen.
|
||||
// See https://en.wikipedia.org/wiki/Canonical_Huffman_code.
|
||||
func buildTable(codelens []byte) *huffman {
|
||||
// Determine the number of codes of each length, and the
|
||||
// maximum length.
|
||||
var count [maxTreePathLen + 1]uint
|
||||
var max byte
|
||||
for _, cl := range codelens {
|
||||
count[cl]++
|
||||
if max < cl {
|
||||
max = cl
|
||||
}
|
||||
}
|
||||
|
||||
if max == 0 {
|
||||
return &huffman{}
|
||||
}
|
||||
|
||||
// Determine the first code of each length.
|
||||
var first [maxTreePathLen + 1]uint
|
||||
code := uint(0)
|
||||
for i := byte(1); i <= max; i++ {
|
||||
code <<= 1
|
||||
first[i] = code
|
||||
code += count[i]
|
||||
}
|
||||
|
||||
if code != 1<<max {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Build a table for code lookup. For code sizes < max,
|
||||
// put all possible suffixes for the code into the table, too.
|
||||
// For max > tablebits, split long codes into additional tables
|
||||
// of suffixes of max-tablebits length.
|
||||
h := &huffman{maxbits: max}
|
||||
if max > tablebits {
|
||||
core := first[tablebits+1] / 2 // Number of codes that fit without extra tables
|
||||
nextra := 1<<tablebits - core // Number of extra entries
|
||||
h.extra = make([][]uint16, nextra)
|
||||
for code := core; code < 1<<tablebits; code++ {
|
||||
h.table[code] = uint16(code - core)
|
||||
h.extra[code-core] = make([]uint16, 1<<(max-tablebits))
|
||||
}
|
||||
}
|
||||
|
||||
for i, cl := range codelens {
|
||||
if cl != 0 {
|
||||
code := first[cl]
|
||||
first[cl]++
|
||||
v := uint16(cl)<<lenshift | uint16(i)
|
||||
if cl <= tablebits {
|
||||
extendedCode := code << (tablebits - cl)
|
||||
for j := uint(0); j < 1<<(tablebits-cl); j++ {
|
||||
h.table[extendedCode+j] = v
|
||||
}
|
||||
} else {
|
||||
prefix := code >> (cl - tablebits)
|
||||
suffix := code & (1<<(cl-tablebits) - 1)
|
||||
extendedCode := suffix << (max - cl)
|
||||
for j := uint(0); j < 1<<(max-cl); j++ {
|
||||
h.extra[h.table[prefix]][extendedCode+j] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// getCode retrieves the next code using the provided
|
||||
// huffman tree. It sets f.err on error.
|
||||
func (f *decompressor) getCode(h *huffman) uint16 {
|
||||
if h.maxbits > 0 {
|
||||
if f.nbits < maxTreePathLen {
|
||||
f.feed()
|
||||
}
|
||||
|
||||
// For codes with length < tablebits, it doesn't matter
|
||||
// what the remainder of the bits used for table lookup
|
||||
// are, since entries with all possible suffixes were
|
||||
// added to the table.
|
||||
c := h.table[f.c>>(32-tablebits)]
|
||||
if !(c >= 1<<lenshift) {
|
||||
// The code is not in c.
|
||||
c = h.extra[c][f.c<<tablebits>>(32-(h.maxbits-tablebits))]
|
||||
}
|
||||
|
||||
n := byte(c >> lenshift)
|
||||
if f.nbits >= n {
|
||||
// Only consume the length of the code, not the maximum
|
||||
// code length.
|
||||
f.c <<= n
|
||||
f.nbits -= n
|
||||
return c & codemask
|
||||
}
|
||||
|
||||
f.fail(io.ErrUnexpectedEOF)
|
||||
return 0
|
||||
}
|
||||
|
||||
// This is an empty tree. It should not be used.
|
||||
f.fail(errCorrupt)
|
||||
return 0
|
||||
}
|
||||
|
||||
// readTree updates the huffman tree path lengths in lens by
|
||||
// reading and decoding lengths from the byte stream. lens
|
||||
// should be prepopulated with the previous block's tree's path
|
||||
// lengths. For the first block, lens should be zero.
|
||||
func (f *decompressor) readTree(lens []byte) error {
|
||||
// Get the pre-tree for the main tree.
|
||||
var pretreeLen [20]byte
|
||||
for i := range pretreeLen {
|
||||
pretreeLen[i] = byte(f.getBits(4))
|
||||
}
|
||||
if f.err != nil {
|
||||
return f.err
|
||||
}
|
||||
h := buildTable(pretreeLen[:])
|
||||
|
||||
// The lengths are encoded as a series of huffman codes
|
||||
// encoded by the pre-tree.
|
||||
for i := 0; i < len(lens); {
|
||||
c := byte(f.getCode(h))
|
||||
if f.err != nil {
|
||||
return f.err
|
||||
}
|
||||
switch {
|
||||
case c <= 16: // length is delta from previous length
|
||||
lens[i] = (lens[i] + 17 - c) % 17
|
||||
i++
|
||||
case c == 17: // next n + 4 lengths are zero
|
||||
zeroes := int(f.getBits(4)) + 4
|
||||
if i+zeroes > len(lens) {
|
||||
return errCorrupt
|
||||
}
|
||||
for j := 0; j < zeroes; j++ {
|
||||
lens[i+j] = 0
|
||||
}
|
||||
i += zeroes
|
||||
case c == 18: // next n + 20 lengths are zero
|
||||
zeroes := int(f.getBits(5)) + 20
|
||||
if i+zeroes > len(lens) {
|
||||
return errCorrupt
|
||||
}
|
||||
for j := 0; j < zeroes; j++ {
|
||||
lens[i+j] = 0
|
||||
}
|
||||
i += zeroes
|
||||
case c == 19: // next n + 4 lengths all have the same value
|
||||
same := int(f.getBits(1)) + 4
|
||||
if i+same > len(lens) {
|
||||
return errCorrupt
|
||||
}
|
||||
c = byte(f.getCode(h))
|
||||
if c > 16 {
|
||||
return errCorrupt
|
||||
}
|
||||
l := (lens[i] + 17 - c) % 17
|
||||
for j := 0; j < same; j++ {
|
||||
lens[i+j] = l
|
||||
}
|
||||
i += same
|
||||
default:
|
||||
return errCorrupt
|
||||
}
|
||||
}
|
||||
|
||||
if f.err != nil {
|
||||
return f.err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *decompressor) readBlockHeader() (byte, uint16, error) {
|
||||
// If the previous block was an unaligned uncompressed block, restore
|
||||
// 2-byte alignment.
|
||||
if f.unaligned {
|
||||
err := f.ensureAtLeast(1)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
f.bo++
|
||||
f.unaligned = false
|
||||
}
|
||||
|
||||
blockType := f.getBits(3)
|
||||
full := f.getBits(1)
|
||||
var blockSize uint16
|
||||
if full != 0 {
|
||||
blockSize = maxBlockSize
|
||||
} else {
|
||||
blockSize = f.getBits(16)
|
||||
if blockSize > maxBlockSize {
|
||||
return 0, 0, errCorrupt
|
||||
}
|
||||
}
|
||||
|
||||
if f.err != nil {
|
||||
return 0, 0, f.err
|
||||
}
|
||||
|
||||
switch blockType {
|
||||
case verbatimBlock, alignedOffsetBlock:
|
||||
// The caller will read the huffman trees.
|
||||
case uncompressedBlock:
|
||||
if f.nbits > 16 {
|
||||
panic("impossible: more than one 16-bit word remains")
|
||||
}
|
||||
|
||||
// Drop the remaining bits in the current 16-bit word
|
||||
// If there are no bits left, discard a full 16-bit word.
|
||||
n := f.nbits
|
||||
if n == 0 {
|
||||
n = 16
|
||||
}
|
||||
|
||||
f.getBits(n)
|
||||
|
||||
// Read the LRU values for the next block.
|
||||
err := f.ensureAtLeast(12)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
f.lru[0] = uint16(binary.LittleEndian.Uint32(f.b[f.bo : f.bo+4]))
|
||||
f.lru[1] = uint16(binary.LittleEndian.Uint32(f.b[f.bo+4 : f.bo+8]))
|
||||
f.lru[2] = uint16(binary.LittleEndian.Uint32(f.b[f.bo+8 : f.bo+12]))
|
||||
f.bo += 12
|
||||
|
||||
default:
|
||||
return 0, 0, errCorrupt
|
||||
}
|
||||
|
||||
return byte(blockType), blockSize, nil
|
||||
}
|
||||
|
||||
// readTrees reads the two or three huffman trees for the current block.
|
||||
// readAligned specifies whether to read the aligned offset tree.
|
||||
func (f *decompressor) readTrees(readAligned bool) (main *huffman, length *huffman, aligned *huffman, err error) {
|
||||
// Aligned offset blocks start with a small aligned offset tree.
|
||||
if readAligned {
|
||||
var alignedLen [8]byte
|
||||
for i := range alignedLen {
|
||||
alignedLen[i] = byte(f.getBits(3))
|
||||
}
|
||||
aligned = buildTable(alignedLen[:])
|
||||
if aligned == nil {
|
||||
return main, length, aligned, errors.New("corrupt")
|
||||
}
|
||||
}
|
||||
|
||||
// The main tree is encoded in two parts.
|
||||
err = f.readTree(f.mainlens[:maincodesplit])
|
||||
if err != nil {
|
||||
return main, length, aligned, err
|
||||
}
|
||||
err = f.readTree(f.mainlens[maincodesplit:])
|
||||
if err != nil {
|
||||
return main, length, aligned, err
|
||||
}
|
||||
|
||||
main = buildTable(f.mainlens[:])
|
||||
if main == nil {
|
||||
return main, length, aligned, errors.New("corrupt")
|
||||
}
|
||||
|
||||
// The length tree is encoding in a single part.
|
||||
err = f.readTree(f.lenlens[:])
|
||||
if err != nil {
|
||||
return main, length, aligned, err
|
||||
}
|
||||
|
||||
length = buildTable(f.lenlens[:])
|
||||
if length == nil {
|
||||
return main, length, aligned, errors.New("corrupt")
|
||||
}
|
||||
|
||||
return main, length, aligned, f.err
|
||||
}
|
||||
|
||||
// readCompressedBlock decodes a compressed block, writing into the window
|
||||
// starting at start and ending at end, and using the provided huffman trees.
|
||||
func (f *decompressor) readCompressedBlock(start, end uint16, hmain, hlength, haligned *huffman) (int, error) {
|
||||
i := start
|
||||
for i < end {
|
||||
main := f.getCode(hmain)
|
||||
if f.err != nil {
|
||||
break
|
||||
}
|
||||
if main < 256 {
|
||||
// Literal byte.
|
||||
f.window[i] = byte(main)
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
// This is a match backward in the window. Determine
|
||||
// the offset and dlength.
|
||||
matchlen := (main - 256) % 8
|
||||
slot := (main - 256) / 8
|
||||
|
||||
// The length is either the low bits of the code,
|
||||
// or if this is 7, is encoded with the length tree.
|
||||
if matchlen == 7 {
|
||||
matchlen += f.getCode(hlength)
|
||||
}
|
||||
matchlen += 2
|
||||
|
||||
var matchoffset uint16
|
||||
if slot < 3 { //nolint:nestif // todo: simplify nested complexity
|
||||
// The offset is one of the LRU values.
|
||||
matchoffset = f.lru[slot]
|
||||
f.lru[slot] = f.lru[0]
|
||||
f.lru[0] = matchoffset
|
||||
} else {
|
||||
// The offset is encoded as a combination of the
|
||||
// slot and more bits from the bit stream.
|
||||
offsetbits := footerBits[slot]
|
||||
var verbatimbits, alignedbits uint16
|
||||
if offsetbits > 0 {
|
||||
if haligned != nil && offsetbits >= 3 {
|
||||
// This is an aligned offset block. Combine
|
||||
// the bits written verbatim with the aligned
|
||||
// offset tree code.
|
||||
verbatimbits = f.getBits(offsetbits-3) * 8
|
||||
alignedbits = f.getCode(haligned)
|
||||
} else {
|
||||
// There are no aligned offset bits to read,
|
||||
// only verbatim bits.
|
||||
verbatimbits = f.getBits(offsetbits)
|
||||
alignedbits = 0
|
||||
}
|
||||
}
|
||||
matchoffset = basePosition[slot] + verbatimbits + alignedbits - 2
|
||||
// Update the LRU cache.
|
||||
f.lru[2] = f.lru[1]
|
||||
f.lru[1] = f.lru[0]
|
||||
f.lru[0] = matchoffset
|
||||
}
|
||||
|
||||
if !(matchoffset <= i && matchlen <= end-i) {
|
||||
f.fail(errCorrupt)
|
||||
break
|
||||
}
|
||||
copyend := i + matchlen
|
||||
for ; i < copyend; i++ {
|
||||
f.window[i] = f.window[i-matchoffset]
|
||||
}
|
||||
}
|
||||
return int(i - start), f.err
|
||||
}
|
||||
|
||||
// readBlock decodes the current block and returns the number of uncompressed bytes.
|
||||
func (f *decompressor) readBlock(start uint16) (int, error) {
|
||||
blockType, size, err := f.readBlockHeader()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if blockType == uncompressedBlock {
|
||||
if size%2 == 1 {
|
||||
// Remember to realign the byte stream at the next block.
|
||||
f.unaligned = true
|
||||
}
|
||||
copied := 0
|
||||
if f.bo < f.bv {
|
||||
copied = int(size)
|
||||
s := int(start)
|
||||
if copied > f.bv-f.bo {
|
||||
copied = f.bv - f.bo
|
||||
}
|
||||
copy(f.window[s:s+copied], f.b[f.bo:f.bo+copied])
|
||||
f.bo += copied
|
||||
}
|
||||
n, err := io.ReadFull(f.r, f.window[start+uint16(copied):start+size])
|
||||
return copied + n, err
|
||||
}
|
||||
|
||||
hmain, hlength, haligned, err := f.readTrees(blockType == alignedOffsetBlock)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return f.readCompressedBlock(start, start+size, hmain, hlength, haligned)
|
||||
}
|
||||
|
||||
// decodeE8 reverses the 0xe8 x86 instruction encoding that was performed
|
||||
// to the uncompressed data before it was compressed.
|
||||
func decodeE8(b []byte, off int64) {
|
||||
if off > maxe8offset || len(b) < 10 {
|
||||
return
|
||||
}
|
||||
for i := 0; i < len(b)-10; i++ {
|
||||
if b[i] == 0xe8 {
|
||||
currentPtr := int32(off) + int32(i)
|
||||
abs := int32(binary.LittleEndian.Uint32(b[i+1 : i+5]))
|
||||
if abs >= -currentPtr && abs < e8filesize {
|
||||
var rel int32
|
||||
if abs >= 0 {
|
||||
rel = abs - currentPtr
|
||||
} else {
|
||||
rel = abs + e8filesize
|
||||
}
|
||||
binary.LittleEndian.PutUint32(b[i+1:i+5], uint32(rel))
|
||||
}
|
||||
i += 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *decompressor) Read(b []byte) (int, error) {
|
||||
// Read and uncompress everything.
|
||||
if f.windowReader == nil {
|
||||
n := 0
|
||||
for n < f.uncompressed {
|
||||
k, err := f.readBlock(uint16(n))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n += k
|
||||
}
|
||||
decodeE8(f.window[:f.uncompressed], 0)
|
||||
f.windowReader = bytes.NewReader(f.window[:f.uncompressed])
|
||||
}
|
||||
|
||||
// Just read directly from the window.
|
||||
return f.windowReader.Read(b)
|
||||
}
|
||||
|
||||
func (*decompressor) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewReader returns a new io.ReadCloser that decompresses a
|
||||
// WIM LZX stream until uncompressedSize bytes have been returned.
|
||||
func NewReader(r io.Reader, uncompressedSize int) (io.ReadCloser, error) {
|
||||
if uncompressedSize > windowSize {
|
||||
return nil, errors.New("uncompressed size is limited to 32KB")
|
||||
}
|
||||
f := &decompressor{
|
||||
lru: [3]uint16{1, 1, 1},
|
||||
uncompressed: uncompressedSize,
|
||||
b: make([]byte, 4096),
|
||||
r: r,
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
+898
@@ -0,0 +1,898 @@
|
||||
//go:build windows || linux
|
||||
// +build windows linux
|
||||
|
||||
// Package wim implements a WIM file parser.
|
||||
//
|
||||
// WIM files are used to distribute Windows file system and container images.
|
||||
// They are documented at https://msdn.microsoft.com/en-us/library/windows/desktop/dd861280.aspx.
|
||||
package wim
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1" //nolint:gosec // not used for secure application
|
||||
"encoding/binary"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode/utf16"
|
||||
)
|
||||
|
||||
// File attribute constants from Windows.
|
||||
//
|
||||
//nolint:revive // var-naming: ALL_CAPS
|
||||
const (
|
||||
FILE_ATTRIBUTE_READONLY = 0x00000001
|
||||
FILE_ATTRIBUTE_HIDDEN = 0x00000002
|
||||
FILE_ATTRIBUTE_SYSTEM = 0x00000004
|
||||
FILE_ATTRIBUTE_DIRECTORY = 0x00000010
|
||||
FILE_ATTRIBUTE_ARCHIVE = 0x00000020
|
||||
FILE_ATTRIBUTE_DEVICE = 0x00000040
|
||||
FILE_ATTRIBUTE_NORMAL = 0x00000080
|
||||
FILE_ATTRIBUTE_TEMPORARY = 0x00000100
|
||||
FILE_ATTRIBUTE_SPARSE_FILE = 0x00000200
|
||||
FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400
|
||||
FILE_ATTRIBUTE_COMPRESSED = 0x00000800
|
||||
FILE_ATTRIBUTE_OFFLINE = 0x00001000
|
||||
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000
|
||||
FILE_ATTRIBUTE_ENCRYPTED = 0x00004000
|
||||
FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x00008000
|
||||
FILE_ATTRIBUTE_VIRTUAL = 0x00010000
|
||||
FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x00020000
|
||||
FILE_ATTRIBUTE_EA = 0x00040000
|
||||
)
|
||||
|
||||
// Windows processor architectures.
|
||||
//
|
||||
//nolint:revive // var-naming: ALL_CAPS
|
||||
const (
|
||||
PROCESSOR_ARCHITECTURE_INTEL = 0
|
||||
PROCESSOR_ARCHITECTURE_MIPS = 1
|
||||
PROCESSOR_ARCHITECTURE_ALPHA = 2
|
||||
PROCESSOR_ARCHITECTURE_PPC = 3
|
||||
PROCESSOR_ARCHITECTURE_SHX = 4
|
||||
PROCESSOR_ARCHITECTURE_ARM = 5
|
||||
PROCESSOR_ARCHITECTURE_IA64 = 6
|
||||
PROCESSOR_ARCHITECTURE_ALPHA64 = 7
|
||||
PROCESSOR_ARCHITECTURE_MSIL = 8
|
||||
PROCESSOR_ARCHITECTURE_AMD64 = 9
|
||||
PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 = 10
|
||||
PROCESSOR_ARCHITECTURE_NEUTRAL = 11
|
||||
PROCESSOR_ARCHITECTURE_ARM64 = 12
|
||||
)
|
||||
|
||||
var wimImageTag = [...]byte{'M', 'S', 'W', 'I', 'M', 0, 0, 0}
|
||||
|
||||
// todo: replace this with pkg/guid.GUID (and add tests to make sure nothing breaks)
|
||||
|
||||
type guid struct {
|
||||
Data1 uint32
|
||||
Data2 uint16
|
||||
Data3 uint16
|
||||
Data4 [8]byte
|
||||
}
|
||||
|
||||
func (g guid) String() string {
|
||||
return fmt.Sprintf("%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
||||
g.Data1,
|
||||
g.Data2,
|
||||
g.Data3,
|
||||
g.Data4[0],
|
||||
g.Data4[1],
|
||||
g.Data4[2],
|
||||
g.Data4[3],
|
||||
g.Data4[4],
|
||||
g.Data4[5],
|
||||
g.Data4[6],
|
||||
g.Data4[7])
|
||||
}
|
||||
|
||||
type resourceDescriptor struct {
|
||||
FlagsAndCompressedSize uint64
|
||||
Offset int64
|
||||
OriginalSize int64
|
||||
}
|
||||
|
||||
type resFlag byte
|
||||
|
||||
//nolint:deadcode,varcheck // need unused variables for iota to work
|
||||
const (
|
||||
resFlagFree resFlag = 1 << iota
|
||||
resFlagMetadata
|
||||
resFlagCompressed
|
||||
resFlagSpanned
|
||||
)
|
||||
|
||||
const validate = false
|
||||
|
||||
const supportedResFlags = resFlagMetadata | resFlagCompressed
|
||||
|
||||
func (r *resourceDescriptor) Flags() resFlag {
|
||||
return resFlag(r.FlagsAndCompressedSize >> 56)
|
||||
}
|
||||
|
||||
func (r *resourceDescriptor) CompressedSize() int64 {
|
||||
return int64(r.FlagsAndCompressedSize & 0xffffffffffffff)
|
||||
}
|
||||
|
||||
func (r *resourceDescriptor) String() string {
|
||||
s := fmt.Sprintf("%d bytes at %d", r.CompressedSize(), r.Offset)
|
||||
if r.Flags()&4 != 0 {
|
||||
s += fmt.Sprintf(" (uncompresses to %d)", r.OriginalSize)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// SHA1Hash contains the SHA1 hash of a file or stream.
|
||||
type SHA1Hash [20]byte
|
||||
|
||||
type streamDescriptor struct {
|
||||
resourceDescriptor
|
||||
PartNumber uint16
|
||||
RefCount uint32
|
||||
Hash SHA1Hash
|
||||
}
|
||||
|
||||
type hdrFlag uint32
|
||||
|
||||
//nolint:deadcode,varcheck // need unused variables for iota to work
|
||||
const (
|
||||
hdrFlagReserved hdrFlag = 1 << iota
|
||||
hdrFlagCompressed
|
||||
hdrFlagReadOnly
|
||||
hdrFlagSpanned
|
||||
hdrFlagResourceOnly
|
||||
hdrFlagMetadataOnly
|
||||
hdrFlagWriteInProgress
|
||||
hdrFlagRpFix
|
||||
)
|
||||
|
||||
//nolint:deadcode,varcheck // need unused variables for iota to work
|
||||
const (
|
||||
hdrFlagCompressReserved hdrFlag = 1 << (iota + 16)
|
||||
hdrFlagCompressXpress
|
||||
hdrFlagCompressLzx
|
||||
)
|
||||
|
||||
const supportedHdrFlags = hdrFlagRpFix | hdrFlagReadOnly | hdrFlagCompressed | hdrFlagCompressLzx
|
||||
|
||||
type wimHeader struct {
|
||||
ImageTag [8]byte
|
||||
Size uint32
|
||||
Version uint32
|
||||
Flags hdrFlag
|
||||
CompressionSize uint32
|
||||
WIMGuid guid
|
||||
PartNumber uint16
|
||||
TotalParts uint16
|
||||
ImageCount uint32
|
||||
OffsetTable resourceDescriptor
|
||||
XMLData resourceDescriptor
|
||||
BootMetadata resourceDescriptor
|
||||
BootIndex uint32
|
||||
Padding uint32
|
||||
Integrity resourceDescriptor
|
||||
Unused [60]byte
|
||||
}
|
||||
|
||||
type securityblockDisk struct {
|
||||
TotalLength uint32
|
||||
NumEntries uint32
|
||||
}
|
||||
|
||||
const securityblockDiskSize = 8
|
||||
|
||||
type direntry struct {
|
||||
Attributes uint32
|
||||
SecurityID uint32
|
||||
SubdirOffset int64
|
||||
Unused1, Unused2 int64
|
||||
CreationTime Filetime
|
||||
LastAccessTime Filetime
|
||||
LastWriteTime Filetime
|
||||
Hash SHA1Hash
|
||||
Padding uint32
|
||||
ReparseHardLink int64
|
||||
StreamCount uint16
|
||||
ShortNameLength uint16
|
||||
FileNameLength uint16
|
||||
}
|
||||
|
||||
var direntrySize = int64(binary.Size(direntry{}) + 8) // includes an 8-byte length prefix
|
||||
|
||||
type streamentry struct {
|
||||
Unused int64
|
||||
Hash SHA1Hash
|
||||
NameLength int16
|
||||
}
|
||||
|
||||
var streamentrySize = int64(binary.Size(streamentry{}) + 8) // includes an 8-byte length prefix
|
||||
|
||||
// Filetime represents a Windows time.
|
||||
type Filetime struct {
|
||||
LowDateTime uint32
|
||||
HighDateTime uint32
|
||||
}
|
||||
|
||||
// Time returns the time as time.Time.
|
||||
func (ft *Filetime) Time() time.Time {
|
||||
// 100-nanosecond intervals since January 1, 1601
|
||||
nsec := int64(ft.HighDateTime)<<32 + int64(ft.LowDateTime)
|
||||
// change starting time to the Epoch (00:00:00 UTC, January 1, 1970)
|
||||
nsec -= 116444736000000000
|
||||
// convert into nanoseconds
|
||||
nsec *= 100
|
||||
return time.Unix(0, nsec)
|
||||
}
|
||||
|
||||
// UnmarshalXML unmarshalls the time from a WIM XML blob.
|
||||
func (ft *Filetime) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
type Time struct {
|
||||
Low string `xml:"LOWPART"`
|
||||
High string `xml:"HIGHPART"`
|
||||
}
|
||||
var t Time
|
||||
err := d.DecodeElement(&t, &start)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
low, err := strconv.ParseUint(t.Low, 0, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
high, err := strconv.ParseUint(t.High, 0, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ft.LowDateTime = uint32(low)
|
||||
ft.HighDateTime = uint32(high)
|
||||
return nil
|
||||
}
|
||||
|
||||
type info struct {
|
||||
Image []ImageInfo `xml:"IMAGE"`
|
||||
}
|
||||
|
||||
// ImageInfo contains information about the image.
|
||||
type ImageInfo struct {
|
||||
Name string `xml:"NAME"`
|
||||
Index int `xml:"INDEX,attr"`
|
||||
CreationTime Filetime `xml:"CREATIONTIME"`
|
||||
ModTime Filetime `xml:"LASTMODIFICATIONTIME"`
|
||||
Windows *WindowsInfo `xml:"WINDOWS"`
|
||||
}
|
||||
|
||||
// WindowsInfo contains information about the Windows installation in the image.
|
||||
type WindowsInfo struct {
|
||||
Arch byte `xml:"ARCH"`
|
||||
ProductName string `xml:"PRODUCTNAME"`
|
||||
EditionID string `xml:"EDITIONID"`
|
||||
InstallationType string `xml:"INSTALLATIONTYPE"`
|
||||
ProductType string `xml:"PRODUCTTYPE"`
|
||||
Languages []string `xml:"LANGUAGES>LANGUAGE"`
|
||||
DefaultLanguage string `xml:"LANGUAGES>DEFAULT"`
|
||||
Version Version `xml:"VERSION"`
|
||||
SystemRoot string `xml:"SYSTEMROOT"`
|
||||
}
|
||||
|
||||
// Version represents a Windows build version.
|
||||
type Version struct {
|
||||
Major int `xml:"MAJOR"`
|
||||
Minor int `xml:"MINOR"`
|
||||
Build int `xml:"BUILD"`
|
||||
SPBuild int `xml:"SPBUILD"`
|
||||
SPLevel int `xml:"SPLEVEL"`
|
||||
}
|
||||
|
||||
// ParseError is returned when the WIM cannot be parsed.
|
||||
type ParseError struct {
|
||||
Oper string
|
||||
Path string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *ParseError) Error() string {
|
||||
if e.Path == "" {
|
||||
return "WIM parse error at " + e.Oper + ": " + e.Err.Error()
|
||||
}
|
||||
return fmt.Sprintf("WIM parse error: %s %s: %s", e.Oper, e.Path, e.Err.Error())
|
||||
}
|
||||
|
||||
func (e *ParseError) Unwrap() error { return e.Err }
|
||||
|
||||
// Reader provides functions to read a WIM file.
|
||||
type Reader struct {
|
||||
hdr wimHeader
|
||||
r io.ReaderAt
|
||||
fileData map[SHA1Hash]resourceDescriptor
|
||||
|
||||
XMLInfo string // The XML information about the WIM.
|
||||
Image []*Image // The WIM's images.
|
||||
}
|
||||
|
||||
// Image represents an image within a WIM file.
|
||||
type Image struct {
|
||||
wim *Reader
|
||||
offset resourceDescriptor
|
||||
sds [][]byte
|
||||
rootOffset int64
|
||||
r io.ReadCloser
|
||||
curOffset int64
|
||||
m sync.Mutex
|
||||
|
||||
ImageInfo
|
||||
}
|
||||
|
||||
// StreamHeader contains alternate data stream metadata.
|
||||
type StreamHeader struct {
|
||||
Name string
|
||||
Hash SHA1Hash
|
||||
Size int64
|
||||
}
|
||||
|
||||
// Stream represents an alternate data stream or reparse point data stream.
|
||||
type Stream struct {
|
||||
StreamHeader
|
||||
wim *Reader
|
||||
offset resourceDescriptor
|
||||
}
|
||||
|
||||
// FileHeader contains file metadata.
|
||||
type FileHeader struct {
|
||||
Name string
|
||||
ShortName string
|
||||
Attributes uint32
|
||||
SecurityDescriptor []byte
|
||||
CreationTime Filetime
|
||||
LastAccessTime Filetime
|
||||
LastWriteTime Filetime
|
||||
Hash SHA1Hash
|
||||
Size int64
|
||||
LinkID int64
|
||||
ReparseTag uint32
|
||||
ReparseReserved uint32
|
||||
}
|
||||
|
||||
// File represents a file or directory in a WIM image.
|
||||
type File struct {
|
||||
FileHeader
|
||||
Streams []*Stream
|
||||
offset resourceDescriptor
|
||||
img *Image
|
||||
subdirOffset int64
|
||||
}
|
||||
|
||||
// NewReader returns a Reader that can be used to read WIM file data.
|
||||
func NewReader(f io.ReaderAt) (*Reader, error) {
|
||||
r := &Reader{r: f}
|
||||
section := io.NewSectionReader(f, 0, 0xffff)
|
||||
err := binary.Read(section, binary.LittleEndian, &r.hdr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if r.hdr.ImageTag != wimImageTag {
|
||||
return nil, &ParseError{Oper: "image tag", Err: errors.New("not a WIM file")}
|
||||
}
|
||||
|
||||
if r.hdr.Flags&^supportedHdrFlags != 0 {
|
||||
return nil, fmt.Errorf("unsupported WIM flags %x", r.hdr.Flags&^supportedHdrFlags)
|
||||
}
|
||||
|
||||
if r.hdr.CompressionSize != 0x8000 {
|
||||
return nil, fmt.Errorf("unsupported compression size %d", r.hdr.CompressionSize)
|
||||
}
|
||||
|
||||
if r.hdr.TotalParts != 1 {
|
||||
return nil, errors.New("multi-part WIM not supported")
|
||||
}
|
||||
|
||||
fileData, images, err := r.readOffsetTable(&r.hdr.OffsetTable)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
xmlinfo, err := r.readXML()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var inf info
|
||||
err = xml.Unmarshal([]byte(xmlinfo), &inf)
|
||||
if err != nil {
|
||||
return nil, &ParseError{Oper: "XML info", Err: err}
|
||||
}
|
||||
|
||||
for i, img := range images {
|
||||
for _, imgInfo := range inf.Image {
|
||||
if imgInfo.Index == i+1 {
|
||||
img.ImageInfo = imgInfo
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.fileData = fileData
|
||||
r.Image = images
|
||||
r.XMLInfo = xmlinfo
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Close releases resources associated with the Reader.
|
||||
func (r *Reader) Close() error {
|
||||
for _, img := range r.Image {
|
||||
img.reset()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Reader) resourceReader(hdr *resourceDescriptor) (io.ReadCloser, error) {
|
||||
return r.resourceReaderWithOffset(hdr, 0)
|
||||
}
|
||||
|
||||
func (r *Reader) resourceReaderWithOffset(hdr *resourceDescriptor, offset int64) (io.ReadCloser, error) {
|
||||
var sr io.ReadCloser
|
||||
section := io.NewSectionReader(r.r, hdr.Offset, hdr.CompressedSize())
|
||||
if hdr.Flags()&resFlagCompressed == 0 {
|
||||
_, _ = section.Seek(offset, 0)
|
||||
sr = io.NopCloser(section)
|
||||
} else {
|
||||
cr, err := newCompressedReader(section, hdr.OriginalSize, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sr = cr
|
||||
}
|
||||
|
||||
return sr, nil
|
||||
}
|
||||
|
||||
func (r *Reader) readResource(hdr *resourceDescriptor) ([]byte, error) {
|
||||
rsrc, err := r.resourceReader(hdr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rsrc.Close()
|
||||
return io.ReadAll(rsrc)
|
||||
}
|
||||
|
||||
func (r *Reader) readXML() (string, error) {
|
||||
if r.hdr.XMLData.CompressedSize() == 0 {
|
||||
return "", nil
|
||||
}
|
||||
rsrc, err := r.resourceReader(&r.hdr.XMLData)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer rsrc.Close()
|
||||
|
||||
xmlData := make([]uint16, r.hdr.XMLData.OriginalSize/2)
|
||||
err = binary.Read(rsrc, binary.LittleEndian, xmlData)
|
||||
if err != nil {
|
||||
return "", &ParseError{Oper: "XML data", Err: err}
|
||||
}
|
||||
|
||||
// The BOM will always indicate little-endian UTF-16.
|
||||
if xmlData[0] != 0xfeff {
|
||||
return "", &ParseError{Oper: "XML data", Err: errors.New("invalid BOM")}
|
||||
}
|
||||
return string(utf16.Decode(xmlData[1:])), nil
|
||||
}
|
||||
|
||||
func (r *Reader) readOffsetTable(res *resourceDescriptor) (map[SHA1Hash]resourceDescriptor, []*Image, error) {
|
||||
fileData := make(map[SHA1Hash]resourceDescriptor)
|
||||
var images []*Image
|
||||
|
||||
offsetTable, err := r.readResource(res)
|
||||
if err != nil {
|
||||
return nil, nil, &ParseError{Oper: "offset table", Err: err}
|
||||
}
|
||||
|
||||
br := bytes.NewReader(offsetTable)
|
||||
for i := 0; ; i++ {
|
||||
var res streamDescriptor
|
||||
err := binary.Read(br, binary.LittleEndian, &res)
|
||||
if err == io.EOF { //nolint:errorlint
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, &ParseError{Oper: "offset table", Err: err}
|
||||
}
|
||||
if res.Flags()&^supportedResFlags != 0 {
|
||||
return nil, nil, &ParseError{Oper: "offset table", Err: errors.New("unsupported resource flag")}
|
||||
}
|
||||
|
||||
// Validation for ad-hoc testing
|
||||
if validate {
|
||||
sec, err := r.resourceReader(&res.resourceDescriptor)
|
||||
if err != nil {
|
||||
panic(fmt.Sprint(i, err))
|
||||
}
|
||||
hash := sha1.New() //nolint:gosec // not used for secure application
|
||||
_, err = io.Copy(hash, sec)
|
||||
sec.Close()
|
||||
if err != nil {
|
||||
panic(fmt.Sprint(i, err))
|
||||
}
|
||||
var cmphash SHA1Hash
|
||||
copy(cmphash[:], hash.Sum(nil))
|
||||
if cmphash != res.Hash {
|
||||
panic(fmt.Sprint(i, "hash mismatch"))
|
||||
}
|
||||
}
|
||||
|
||||
if res.Flags()&resFlagMetadata != 0 {
|
||||
image := &Image{
|
||||
wim: r,
|
||||
offset: res.resourceDescriptor,
|
||||
}
|
||||
images = append(images, image)
|
||||
} else {
|
||||
fileData[res.Hash] = res.resourceDescriptor
|
||||
}
|
||||
}
|
||||
|
||||
if len(images) != int(r.hdr.ImageCount) {
|
||||
return nil, nil, &ParseError{Oper: "offset table", Err: errors.New("mismatched image count")}
|
||||
}
|
||||
|
||||
return fileData, images, nil
|
||||
}
|
||||
|
||||
func (*Reader) readSecurityDescriptors(rsrc io.Reader) (sds [][]byte, n int64, err error) {
|
||||
var secBlock securityblockDisk
|
||||
err = binary.Read(rsrc, binary.LittleEndian, &secBlock)
|
||||
if err != nil {
|
||||
return sds, 0, &ParseError{Oper: "security table", Err: err}
|
||||
}
|
||||
|
||||
n += securityblockDiskSize
|
||||
|
||||
secSizes := make([]int64, secBlock.NumEntries)
|
||||
err = binary.Read(rsrc, binary.LittleEndian, &secSizes)
|
||||
if err != nil {
|
||||
return sds, n, &ParseError{Oper: "security table sizes", Err: err}
|
||||
}
|
||||
|
||||
n += int64(secBlock.NumEntries * 8)
|
||||
|
||||
sds = make([][]byte, secBlock.NumEntries)
|
||||
for i, size := range secSizes {
|
||||
sd := make([]byte, size&0xffffffff)
|
||||
_, err = io.ReadFull(rsrc, sd)
|
||||
if err != nil {
|
||||
return sds, n, &ParseError{Oper: "security descriptor", Err: err}
|
||||
}
|
||||
n += int64(len(sd))
|
||||
sds[i] = sd
|
||||
}
|
||||
|
||||
secsize := int64((secBlock.TotalLength + 7) &^ 7)
|
||||
if n > secsize {
|
||||
return sds, n, &ParseError{Oper: "security descriptor", Err: errors.New("security descriptor table too small")}
|
||||
}
|
||||
|
||||
_, err = io.CopyN(io.Discard, rsrc, secsize-n)
|
||||
if err != nil {
|
||||
return sds, n, err
|
||||
}
|
||||
|
||||
n = secsize
|
||||
return sds, n, nil
|
||||
}
|
||||
|
||||
// Open parses the image and returns the root directory.
|
||||
func (img *Image) Open() (*File, error) {
|
||||
if img.sds == nil {
|
||||
rsrc, err := img.wim.resourceReaderWithOffset(&img.offset, img.rootOffset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sds, n, err := img.wim.readSecurityDescriptors(rsrc)
|
||||
if err != nil {
|
||||
rsrc.Close()
|
||||
return nil, err
|
||||
}
|
||||
img.sds = sds
|
||||
img.r = rsrc
|
||||
img.rootOffset = n
|
||||
img.curOffset = n
|
||||
}
|
||||
|
||||
f, err := img.readdir(img.rootOffset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(f) != 1 {
|
||||
return nil, &ParseError{Oper: "root directory", Err: errors.New("expected exactly 1 root directory entry")}
|
||||
}
|
||||
return f[0], err
|
||||
}
|
||||
|
||||
func (img *Image) reset() {
|
||||
if img.r != nil {
|
||||
img.r.Close()
|
||||
img.r = nil
|
||||
}
|
||||
img.curOffset = -1
|
||||
}
|
||||
|
||||
func (img *Image) readdir(offset int64) ([]*File, error) {
|
||||
img.m.Lock()
|
||||
defer img.m.Unlock()
|
||||
|
||||
if offset < img.curOffset || offset > img.curOffset+chunkSize {
|
||||
// Reset to seek backward or to seek forward very far.
|
||||
img.reset()
|
||||
}
|
||||
if img.r == nil {
|
||||
rsrc, err := img.wim.resourceReaderWithOffset(&img.offset, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img.r = rsrc
|
||||
img.curOffset = offset
|
||||
}
|
||||
if offset > img.curOffset {
|
||||
_, err := io.CopyN(io.Discard, img.r, offset-img.curOffset)
|
||||
if err != nil {
|
||||
img.reset()
|
||||
if err == io.EOF { //nolint:errorlint
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var entries []*File
|
||||
for {
|
||||
e, n, err := img.readNextEntry(img.r)
|
||||
img.curOffset += n
|
||||
if err == io.EOF { //nolint:errorlint
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
img.reset()
|
||||
return nil, err
|
||||
}
|
||||
entries = append(entries, e)
|
||||
}
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
func (img *Image) readNextEntry(r io.Reader) (*File, int64, error) {
|
||||
var length int64
|
||||
err := binary.Read(r, binary.LittleEndian, &length)
|
||||
if err != nil {
|
||||
return nil, 0, &ParseError{Oper: "directory length check", Err: err}
|
||||
}
|
||||
|
||||
if length == 0 {
|
||||
return nil, 8, io.EOF
|
||||
}
|
||||
|
||||
left := length
|
||||
if left < direntrySize {
|
||||
return nil, 0, &ParseError{Oper: "directory entry", Err: errors.New("size too short")}
|
||||
}
|
||||
|
||||
var dentry direntry
|
||||
err = binary.Read(r, binary.LittleEndian, &dentry)
|
||||
if err != nil {
|
||||
return nil, 0, &ParseError{Oper: "directory entry", Err: err}
|
||||
}
|
||||
|
||||
left -= direntrySize
|
||||
|
||||
namesLen := int64(dentry.FileNameLength + 2 + dentry.ShortNameLength)
|
||||
if left < namesLen {
|
||||
return nil, 0, &ParseError{Oper: "directory entry", Err: errors.New("size too short for names")}
|
||||
}
|
||||
|
||||
names := make([]uint16, namesLen/2)
|
||||
err = binary.Read(r, binary.LittleEndian, names)
|
||||
if err != nil {
|
||||
return nil, 0, &ParseError{Oper: "file name", Err: err}
|
||||
}
|
||||
|
||||
left -= namesLen
|
||||
|
||||
var name, shortName string
|
||||
if dentry.FileNameLength > 0 {
|
||||
name = string(utf16.Decode(names[:dentry.FileNameLength/2]))
|
||||
}
|
||||
|
||||
if dentry.ShortNameLength > 0 {
|
||||
shortName = string(utf16.Decode(names[dentry.FileNameLength/2+1:]))
|
||||
}
|
||||
|
||||
var offset resourceDescriptor
|
||||
zerohash := SHA1Hash{}
|
||||
if dentry.Hash != zerohash {
|
||||
var ok bool
|
||||
offset, ok = img.wim.fileData[dentry.Hash]
|
||||
if !ok {
|
||||
return nil, 0, &ParseError{
|
||||
Oper: "directory entry",
|
||||
Path: name,
|
||||
Err: fmt.Errorf("could not find file data matching hash %#v", dentry),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f := &File{
|
||||
FileHeader: FileHeader{
|
||||
Attributes: dentry.Attributes,
|
||||
CreationTime: dentry.CreationTime,
|
||||
LastAccessTime: dentry.LastAccessTime,
|
||||
LastWriteTime: dentry.LastWriteTime,
|
||||
Hash: dentry.Hash,
|
||||
Size: offset.OriginalSize,
|
||||
Name: name,
|
||||
ShortName: shortName,
|
||||
},
|
||||
|
||||
offset: offset,
|
||||
img: img,
|
||||
subdirOffset: dentry.SubdirOffset,
|
||||
}
|
||||
|
||||
isDir := false
|
||||
|
||||
if dentry.Attributes&FILE_ATTRIBUTE_REPARSE_POINT == 0 {
|
||||
f.LinkID = dentry.ReparseHardLink
|
||||
if dentry.Attributes&FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
isDir = true
|
||||
}
|
||||
} else {
|
||||
f.ReparseTag = uint32(dentry.ReparseHardLink)
|
||||
f.ReparseReserved = uint32(dentry.ReparseHardLink >> 32)
|
||||
}
|
||||
|
||||
if isDir && f.subdirOffset == 0 {
|
||||
return nil, 0, &ParseError{Oper: "directory entry", Path: name, Err: errors.New("no subdirectory data for directory")}
|
||||
} else if !isDir && f.subdirOffset != 0 {
|
||||
return nil, 0, &ParseError{Oper: "directory entry", Path: name, Err: errors.New("unexpected subdirectory data for non-directory")}
|
||||
}
|
||||
|
||||
if dentry.SecurityID != 0xffffffff {
|
||||
f.SecurityDescriptor = img.sds[dentry.SecurityID]
|
||||
}
|
||||
|
||||
_, err = io.CopyN(io.Discard, r, left)
|
||||
if err != nil {
|
||||
if err == io.EOF { //nolint:errorlint
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if dentry.StreamCount > 0 {
|
||||
var streams []*Stream
|
||||
for i := uint16(0); i < dentry.StreamCount; i++ {
|
||||
s, n, err := img.readNextStream(r)
|
||||
length += n
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
// The first unnamed stream should be treated as the file stream.
|
||||
if i == 0 && s.Name == "" {
|
||||
f.Hash = s.Hash
|
||||
f.Size = s.Size
|
||||
f.offset = s.offset
|
||||
} else if s.Name != "" {
|
||||
streams = append(streams, s)
|
||||
}
|
||||
}
|
||||
f.Streams = streams
|
||||
}
|
||||
|
||||
if dentry.Attributes&FILE_ATTRIBUTE_REPARSE_POINT != 0 && f.Size == 0 {
|
||||
return nil, 0, &ParseError{
|
||||
Oper: "directory entry",
|
||||
Path: name,
|
||||
Err: errors.New("reparse point is missing reparse stream"),
|
||||
}
|
||||
}
|
||||
|
||||
return f, length, nil
|
||||
}
|
||||
|
||||
func (img *Image) readNextStream(r io.Reader) (*Stream, int64, error) {
|
||||
var length int64
|
||||
err := binary.Read(r, binary.LittleEndian, &length)
|
||||
if err != nil {
|
||||
if err == io.EOF { //nolint:errorlint
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil, 0, &ParseError{Oper: "stream length check", Err: err}
|
||||
}
|
||||
|
||||
left := length
|
||||
if left < streamentrySize {
|
||||
return nil, 0, &ParseError{Oper: "stream entry", Err: errors.New("size too short")}
|
||||
}
|
||||
|
||||
var sentry streamentry
|
||||
err = binary.Read(r, binary.LittleEndian, &sentry)
|
||||
if err != nil {
|
||||
return nil, 0, &ParseError{Oper: "stream entry", Err: err}
|
||||
}
|
||||
|
||||
left -= streamentrySize
|
||||
|
||||
if left < int64(sentry.NameLength) {
|
||||
return nil, 0, &ParseError{Oper: "stream entry", Err: errors.New("size too short for name")}
|
||||
}
|
||||
|
||||
names := make([]uint16, sentry.NameLength/2)
|
||||
err = binary.Read(r, binary.LittleEndian, names)
|
||||
if err != nil {
|
||||
return nil, 0, &ParseError{Oper: "file name", Err: err}
|
||||
}
|
||||
|
||||
left -= int64(sentry.NameLength)
|
||||
name := string(utf16.Decode(names))
|
||||
|
||||
var offset resourceDescriptor
|
||||
if sentry.Hash != (SHA1Hash{}) {
|
||||
var ok bool
|
||||
offset, ok = img.wim.fileData[sentry.Hash]
|
||||
if !ok {
|
||||
return nil, 0, &ParseError{
|
||||
Oper: "stream entry",
|
||||
Path: name,
|
||||
Err: fmt.Errorf("could not find file data matching hash %v", sentry.Hash),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s := &Stream{
|
||||
StreamHeader: StreamHeader{
|
||||
Hash: sentry.Hash,
|
||||
Size: offset.OriginalSize,
|
||||
Name: name,
|
||||
},
|
||||
wim: img.wim,
|
||||
offset: offset,
|
||||
}
|
||||
|
||||
_, err = io.CopyN(io.Discard, r, left)
|
||||
if err != nil {
|
||||
if err == io.EOF { //nolint:errorlint
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return s, length, nil
|
||||
}
|
||||
|
||||
// Open returns an io.ReadCloser that can be used to read the stream's contents.
|
||||
func (s *Stream) Open() (io.ReadCloser, error) {
|
||||
return s.wim.resourceReader(&s.offset)
|
||||
}
|
||||
|
||||
// Open returns an io.ReadCloser that can be used to read the file's contents.
|
||||
func (f *File) Open() (io.ReadCloser, error) {
|
||||
return f.img.wim.resourceReader(&f.offset)
|
||||
}
|
||||
|
||||
// Readdir reads the directory entries.
|
||||
func (f *File) Readdir() ([]*File, error) {
|
||||
if !f.IsDir() {
|
||||
return nil, errors.New("not a directory")
|
||||
}
|
||||
return f.img.readdir(f.subdirOffset)
|
||||
}
|
||||
|
||||
// IsDir returns whether the given file is a directory. It returns false when it
|
||||
// is a directory reparse point.
|
||||
func (f *FileHeader) IsDir() bool {
|
||||
return f.Attributes&(FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_DIRECTORY
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
Copyright (c) 2019-2020, Kamil Domański and contributors
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
## iso9660
|
||||
[](https://pkg.go.dev/github.com/kdomanski/iso9660)
|
||||
[](https://codecov.io/gh/kdomanski/iso9660)
|
||||
[](https://goreportcard.com/report/github.com/kdomanski/iso9660)
|
||||
|
||||
A package for reading and creating ISO9660
|
||||
|
||||
Joliet extension is **NOT** supported.
|
||||
|
||||
Experimental support for reading Rock Ridge extension is currently in the works.
|
||||
If you are experiencing issues, please use the v0.3 release, which ignores Rock Ridge.
|
||||
|
||||
## References for the format:
|
||||
- [ECMA-119 1st edition (December 1986)](https://www.ecma-international.org/wp-content/uploads/ECMA-119_1st_edition_december_1986.pdf) ([Web Archive link](http://web.archive.org/web/20210122025258/https://www.ecma-international.org/wp-content/uploads/ECMA-119_1st_edition_december_1986.pdf))
|
||||
- [ECMA-119 2nd edition (December 1987)](https://www.ecma-international.org/wp-content/uploads/ECMA-119_2nd_edition_december_1987.pdf) ([Web Archive link](http://web.archive.org/web/20210418211711/https://www.ecma-international.org/wp-content/uploads/ECMA-119_2nd_edition_december_1987.pdf))
|
||||
- [ECMA-119 3rd edition (December 2017)](https://www.ecma-international.org/wp-content/uploads/ECMA-119_3rd_edition_december_2017.pdf) ([Web Archive link](http://web.archive.org/web/20210527165925/https://www.ecma-international.org/wp-content/uploads/ECMA-119_3rd_edition_december_2017.pdf))
|
||||
- [ECMA-119 4th edition (June 2019)](https://www.ecma-international.org/wp-content/uploads/ECMA-119_4th_edition_june_2019.pdf) ([Web Archive link](https://www.ecma-international.org/wp-content/uploads/ECMA-119_4th_edition_june_2019.pdf))
|
||||
- [Rock Ridge Interchange Protocol](http://www.nextcomputers.org/NeXTfiles/Projects/CD-ROM/Rock_Ridge_Interchange_Protocol.pdf) ([Web Archive link](http://web.archive.org/web/20071017082049/http://www.nextcomputers.org/NeXTfiles/Projects/CD-ROM/Rock_Ridge_Interchange_Protocol.pdf))
|
||||
- [System Use Sharing Protocol v1.12](http://aminet.net/package/docs/misc/RRIP)
|
||||
|
||||
## Examples
|
||||
|
||||
### Extracting an ISO
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/kdomanski/iso9660/util"
|
||||
)
|
||||
|
||||
func main() {
|
||||
f, err := os.Open("/home/user/myImage.iso")
|
||||
if err != nil {
|
||||
log.Fatalf("failed to open file: %s", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err = util.ExtractImageToDirectory(f, "/home/user/target_dir"); err != nil {
|
||||
log.Fatalf("failed to extract image: %s", err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Creating an ISO
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/kdomanski/iso9660"
|
||||
)
|
||||
|
||||
func main() {
|
||||
writer, err := iso9660.NewWriter()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create writer: %s", err)
|
||||
}
|
||||
defer writer.Cleanup()
|
||||
|
||||
f, err := os.Open("/home/user/myFile.txt")
|
||||
if err != nil {
|
||||
log.Fatalf("failed to open file: %s", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
err = writer.AddFile(f, "folder/MYFILE.TXT")
|
||||
if err != nil {
|
||||
log.Fatalf("failed to add file: %s", err)
|
||||
}
|
||||
|
||||
outputFile, err := os.OpenFile("/home/user/output.iso", os.O_WRONLY | os.O_TRUNC | os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create file: %s", err)
|
||||
}
|
||||
|
||||
err = writer.WriteTo(outputFile, "testvol")
|
||||
if err != nil {
|
||||
log.Fatalf("failed to write ISO image: %s", err)
|
||||
}
|
||||
|
||||
err = outputFile.Close()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to close output file: %s", err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Recursively create an ISO image from the given directories
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/kdomanski/iso9660"
|
||||
)
|
||||
|
||||
func main() {
|
||||
writer, err := iso9660.NewWriter()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create writer: %s", err)
|
||||
}
|
||||
defer writer.Cleanup()
|
||||
|
||||
isoFile, err := os.OpenFile("C:/output.iso", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create file: %s", err)
|
||||
}
|
||||
defer isoFile.Close()
|
||||
|
||||
prefix := "F:\\" // the prefix to remove in the output iso file
|
||||
sourceFolders := []string{"F:\\test1", "F:\\test2"} // the given directories to create an ISO file from
|
||||
|
||||
for _, folderName := range sourceFolders {
|
||||
folderPath := strings.Join([]string{prefix, folderName}, "/")
|
||||
|
||||
walk_err := filepath.Walk(folderPath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
log.Fatalf("walk: %s", err)
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
outputPath := strings.TrimPrefix(path, prefix) // remove the source drive name
|
||||
fmt.Printf("Adding file: %s\n", outputPath)
|
||||
|
||||
fileToAdd, err := os.Open(path)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to open file: %s", err)
|
||||
}
|
||||
defer fileToAdd.Close()
|
||||
|
||||
err = writer.AddFile(fileToAdd, outputPath)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to add file: %s", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if walk_err != nil {
|
||||
log.Fatalf("%s", walk_err)
|
||||
}
|
||||
}
|
||||
|
||||
err = writer.WriteTo(isoFile, "Test")
|
||||
if err != nil {
|
||||
log.Fatalf("failed to write ISO image: %s", err)
|
||||
}
|
||||
}
|
||||
```
|
||||
+292
@@ -0,0 +1,292 @@
|
||||
package iso9660
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Image is a wrapper around an image file that allows reading its ISO9660 data
|
||||
type Image struct {
|
||||
ra io.ReaderAt
|
||||
volumeDescriptors []volumeDescriptor
|
||||
}
|
||||
|
||||
// OpenImage returns an Image reader reating from a given file
|
||||
func OpenImage(ra io.ReaderAt) (*Image, error) {
|
||||
i := &Image{ra: ra}
|
||||
|
||||
if err := i.readVolumes(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (i *Image) readVolumes() error {
|
||||
buffer := make([]byte, sectorSize)
|
||||
// skip the 16 sectors of system area
|
||||
for sector := 16; ; sector++ {
|
||||
if _, err := i.ra.ReadAt(buffer, int64(sector)*int64(sectorSize)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var vd volumeDescriptor
|
||||
if err := vd.UnmarshalBinary(buffer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// NOTE: the instance of the root Directory Record that appears
|
||||
// in the Primary Volume Descriptor cannot contain a System Use
|
||||
// field. See the SUSP standard.
|
||||
|
||||
i.volumeDescriptors = append(i.volumeDescriptors, vd)
|
||||
if vd.Header.Type == volumeTypeTerminator {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RootDir returns the File structure corresponding to the root directory
|
||||
// of the first primary volume
|
||||
func (i *Image) RootDir() (*File, error) {
|
||||
for _, vd := range i.volumeDescriptors {
|
||||
if vd.Type() == volumeTypePrimary {
|
||||
return &File{de: vd.Primary.RootDirectoryEntry, ra: i.ra, children: nil, isRootDir: true}, nil
|
||||
}
|
||||
}
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
|
||||
// RootDir returns the label of the first Primary Volume
|
||||
func (i *Image) Label() (string, error) {
|
||||
for _, vd := range i.volumeDescriptors {
|
||||
if vd.Type() == volumeTypePrimary {
|
||||
return string(vd.Primary.VolumeIdentifier), nil
|
||||
}
|
||||
}
|
||||
return "", os.ErrNotExist
|
||||
}
|
||||
|
||||
// File is a os.FileInfo-compatible wrapper around an ISO9660 directory entry
|
||||
type File struct {
|
||||
ra io.ReaderAt
|
||||
de *DirectoryEntry
|
||||
children []*File
|
||||
isRootDir bool
|
||||
susp *SUSPMetadata
|
||||
}
|
||||
|
||||
var _ os.FileInfo = &File{}
|
||||
|
||||
func (f *File) hasRockRidge() bool {
|
||||
return f.susp != nil && f.susp.HasRockRidge
|
||||
}
|
||||
|
||||
// IsDir returns true if the entry is a directory or false otherwise
|
||||
func (f *File) IsDir() bool {
|
||||
if f.hasRockRidge() {
|
||||
if mode, err := f.de.SystemUseEntries.GetPosixAttr(); err == nil {
|
||||
return mode&os.ModeDir != 0
|
||||
}
|
||||
}
|
||||
|
||||
return f.de.FileFlags&dirFlagDir != 0
|
||||
}
|
||||
|
||||
// ModTime returns the entry's recording time
|
||||
func (f *File) ModTime() time.Time {
|
||||
return time.Time(f.de.RecordingDateTime)
|
||||
}
|
||||
|
||||
// Mode returns file mode when available.
|
||||
// Otherwise it returns os.FileMode flag set with the os.ModeDir flag enabled in case of directories.
|
||||
func (f *File) Mode() os.FileMode {
|
||||
if f.hasRockRidge() {
|
||||
if mode, err := f.de.SystemUseEntries.GetPosixAttr(); err == nil {
|
||||
return mode
|
||||
}
|
||||
}
|
||||
|
||||
var mode os.FileMode
|
||||
if f.IsDir() {
|
||||
mode |= os.ModeDir
|
||||
}
|
||||
return mode
|
||||
}
|
||||
|
||||
// Name returns the base name of the given entry
|
||||
func (f *File) Name() string {
|
||||
if f.hasRockRidge() {
|
||||
if name := f.de.SystemUseEntries.GetRockRidgeName(); name != "" {
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
if f.IsDir() {
|
||||
return f.de.Identifier
|
||||
}
|
||||
|
||||
// drop the version part
|
||||
// assume only one ';'
|
||||
fileIdentifier := strings.Split(f.de.Identifier, ";")[0]
|
||||
|
||||
// split into filename and extension
|
||||
// assume only only one '.'
|
||||
splitFileIdentifier := strings.Split(fileIdentifier, ".")
|
||||
|
||||
// there's no dot in the name, thus no extension
|
||||
if len(splitFileIdentifier) == 1 {
|
||||
return splitFileIdentifier[0]
|
||||
}
|
||||
|
||||
// extension is empty, return just the name without a dot
|
||||
if len(splitFileIdentifier[1]) == 0 {
|
||||
return splitFileIdentifier[0]
|
||||
}
|
||||
|
||||
// return file with extension
|
||||
return fileIdentifier
|
||||
}
|
||||
|
||||
// Size returns the size in bytes of the extent occupied by the file or directory
|
||||
func (f *File) Size() int64 {
|
||||
return int64(f.de.ExtentLength)
|
||||
}
|
||||
|
||||
// Sys returns nil
|
||||
func (f *File) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAllChildren returns the children entries in case of a directory
|
||||
// or an error in case of a file. It includes the "." and ".." entries.
|
||||
func (f *File) GetAllChildren() ([]*File, error) {
|
||||
if !f.IsDir() {
|
||||
return nil, fmt.Errorf("%s is not a directory", f.Name())
|
||||
}
|
||||
|
||||
if f.children != nil {
|
||||
return f.children, nil
|
||||
}
|
||||
|
||||
baseOffset := uint32(f.de.ExtentLocation) * sectorSize
|
||||
|
||||
buffer := make([]byte, sectorSize)
|
||||
for bytesProcessed := uint32(0); bytesProcessed < uint32(f.de.ExtentLength); bytesProcessed += sectorSize {
|
||||
if _, err := f.ra.ReadAt(buffer, int64(baseOffset+bytesProcessed)); err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
for i := uint32(0); i < sectorSize; {
|
||||
entryLength := uint32(buffer[i])
|
||||
if entryLength == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if i+entryLength > sectorSize {
|
||||
return nil, fmt.Errorf("reading directory entries: DE outside of sector boundries")
|
||||
}
|
||||
|
||||
newDE := &DirectoryEntry{}
|
||||
if err := newDE.UnmarshalBinary(buffer[i : i+entryLength]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Is this a root directory '.' record?
|
||||
if f.isRootDir && newDE.Identifier == string([]byte{0}) {
|
||||
newDE.SystemUseEntries, _ = splitSystemUseEntries(newDE.SystemUse, f.ra)
|
||||
|
||||
// get the SP record
|
||||
if len(newDE.SystemUseEntries) > 0 && newDE.SystemUseEntries[0].Type() == "SP" {
|
||||
sprecord, err := SPRecordDecode(newDE.SystemUseEntries[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid SP record: %w", err)
|
||||
}
|
||||
|
||||
hasRockRidge, err := suspHasRockRidge(newDE.SystemUseEntries)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check for Rock Ridge extension: %w", err)
|
||||
}
|
||||
|
||||
// save SUSP offset from the SP record
|
||||
f.susp = &SUSPMetadata{
|
||||
Offset: sprecord.BytesSkipped,
|
||||
HasRockRidge: hasRockRidge,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// are we on a volume with SUSP?
|
||||
if f.susp != nil {
|
||||
// Ignore error if some of the SUSP data is malformed. Just take the valid part.
|
||||
offsetSystemUse := newDE.SystemUse[f.susp.Offset:]
|
||||
newDE.SystemUseEntries, _ = splitSystemUseEntries(offsetSystemUse, f.ra)
|
||||
}
|
||||
}
|
||||
|
||||
i += entryLength
|
||||
|
||||
newFile := &File{ra: f.ra,
|
||||
de: newDE,
|
||||
children: nil,
|
||||
susp: f.susp.Clone(),
|
||||
}
|
||||
|
||||
f.children = append(f.children, newFile)
|
||||
}
|
||||
}
|
||||
|
||||
return f.children, nil
|
||||
}
|
||||
|
||||
// GetChildren returns the children entries in case of a directory
|
||||
// or an error in case of a file. It does NOT include the "." and ".." entries.
|
||||
func (f *File) GetChildren() ([]*File, error) {
|
||||
children, err := f.GetAllChildren()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filteredChildren := make([]*File, 0, len(children)-2)
|
||||
for _, child := range children {
|
||||
if child.de.Identifier == string([]byte{0}) || child.de.Identifier == string([]byte{1}) {
|
||||
continue
|
||||
}
|
||||
|
||||
filteredChildren = append(filteredChildren, child)
|
||||
}
|
||||
|
||||
return filteredChildren, nil
|
||||
}
|
||||
|
||||
// GetDotEntry returns the "." entry of a directory
|
||||
// or an error in case of a file.
|
||||
func (f *File) GetDotEntry() (*File, error) {
|
||||
children, err := f.GetAllChildren()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, child := range children {
|
||||
if child.de.Identifier == string([]byte{0}) {
|
||||
return child, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Reader returns a reader that allows to read the file's data.
|
||||
// If File is a directory, it returns nil.
|
||||
func (f *File) Reader() io.Reader {
|
||||
if f.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
baseOffset := int64(f.de.ExtentLocation) * int64(sectorSize)
|
||||
return io.NewSectionReader(f.ra, baseOffset, int64(f.de.ExtentLength))
|
||||
}
|
||||
+658
@@ -0,0 +1,658 @@
|
||||
package iso9660
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
primaryVolumeDirectoryIdentifierMaxLength = 31 // ECMA-119 7.6.3
|
||||
primaryVolumeFileIdentifierMaxLength = 30 // ECMA-119 7.5
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrFileTooLarge is returned when trying to process a file of size greater
|
||||
// than 4GB, which due to the 32-bit address limitation is not possible
|
||||
// except with ISO 9660-Level 3
|
||||
ErrFileTooLarge = errors.New("file is exceeding the maximum file size of 4GB")
|
||||
)
|
||||
|
||||
// ImageWriter is responsible for staging an image's contents
|
||||
// and writing them to an image.
|
||||
type ImageWriter struct {
|
||||
stagingDir string
|
||||
}
|
||||
|
||||
// NewWriter creates a new ImageWrite and initializes its temporary staging dir.
|
||||
// Cleanup should be called after the ImageWriter is no longer needed.
|
||||
func NewWriter() (*ImageWriter, error) {
|
||||
tmp, err := os.MkdirTemp("", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ImageWriter{stagingDir: tmp}, nil
|
||||
}
|
||||
|
||||
// Cleanup deletes the underlying temporary staging directory of an ImageWriter.
|
||||
// It can be called multiple times without issues.
|
||||
func (iw *ImageWriter) Cleanup() error {
|
||||
if iw.stagingDir == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := os.RemoveAll(iw.stagingDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iw.stagingDir = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddFile adds a file to the ImageWriter's staging area.
|
||||
// All path components are mangled to match basic ISO9660 filename requirements.
|
||||
func (iw *ImageWriter) AddFile(data io.Reader, filePath string) error {
|
||||
directoryPath, fileName := manglePath(filePath)
|
||||
|
||||
if err := os.MkdirAll(path.Join(iw.stagingDir, directoryPath), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(path.Join(iw.stagingDir, directoryPath, fileName), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = io.Copy(f, data)
|
||||
return err
|
||||
}
|
||||
|
||||
func failIfSymlink(path string) error {
|
||||
info, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.Mode()&os.ModeSymlink != 0 {
|
||||
return fmt.Errorf("%q is a symlink - these are not yet supported", path)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddLocalFile adds a file identified by its path to the ImageWriter's staging area.
|
||||
func (iw *ImageWriter) AddLocalFile(origin, target string) error {
|
||||
if err := failIfSymlink(origin); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
directoryPath, fileName := manglePath(target)
|
||||
|
||||
if err := os.MkdirAll(path.Join(iw.stagingDir, directoryPath), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// try to hardlink file to staging area before copying.
|
||||
stagedFile := path.Join(iw.stagingDir, directoryPath, fileName)
|
||||
if err := os.Remove(stagedFile); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.Link(origin, stagedFile); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
f, err := os.Open(origin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
return iw.AddFile(f, target)
|
||||
}
|
||||
|
||||
func ensureIsDirectory(path string) error {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fileinfo, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !fileinfo.IsDir() {
|
||||
return fmt.Errorf("%q is not a directory", path)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddLocalDirectory adds a directory recursively to the ImageWriter's staging area.
|
||||
func (iw *ImageWriter) AddLocalDirectory(origin, target string) error {
|
||||
if err := ensureIsDirectory(origin); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
walkfn := func(path string, info os.FileInfo, err error) error {
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
relPath := path[len(origin):] // We need the path to be relative to the origin.
|
||||
return iw.AddLocalFile(path, filepath.Join(target, relPath))
|
||||
}
|
||||
|
||||
return filepath.Walk(origin, walkfn)
|
||||
}
|
||||
|
||||
func manglePath(input string) (string, string) {
|
||||
input = posixifyPath(input)
|
||||
|
||||
nonEmptySegments := splitPath(input)
|
||||
|
||||
dirSegments := nonEmptySegments[:len(nonEmptySegments)-1]
|
||||
name := nonEmptySegments[len(nonEmptySegments)-1]
|
||||
|
||||
for i := 0; i < len(dirSegments); i++ {
|
||||
dirSegments[i] = mangleDirectoryName(dirSegments[i])
|
||||
}
|
||||
name = mangleFileName(name)
|
||||
|
||||
return path.Join(dirSegments...), name
|
||||
}
|
||||
|
||||
// Converts given path to Posix (replacing \ with /)
|
||||
//
|
||||
// @param {string} givenPath Path to convert
|
||||
//
|
||||
// @returns {string} Converted filepath
|
||||
func posixifyPath(path string) string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return strings.ReplaceAll(path, "\\", "/")
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func splitPath(input string) []string {
|
||||
rawSegments := strings.Split(input, "/")
|
||||
var nonEmptySegments []string
|
||||
for _, s := range rawSegments {
|
||||
if len(s) > 0 {
|
||||
nonEmptySegments = append(nonEmptySegments, s)
|
||||
}
|
||||
}
|
||||
return nonEmptySegments
|
||||
}
|
||||
|
||||
// See ECMA-119 7.5
|
||||
func mangleFileName(input string) string {
|
||||
// https://github.com/torvalds/linux/blob/v5.6/fs/isofs/dir.c#L29
|
||||
input = strings.ToLower(input)
|
||||
split := strings.Split(input, ".")
|
||||
|
||||
version := "1"
|
||||
var filename, extension string
|
||||
if len(split) == 1 {
|
||||
filename = split[0]
|
||||
} else {
|
||||
filename = strings.Join(split[:len(split)-1], "_")
|
||||
extension = split[len(split)-1]
|
||||
}
|
||||
|
||||
// enough characters for the `.ignition` extension
|
||||
extension = mangleD1String(extension, 8)
|
||||
|
||||
maxRemainingFilenameLength := primaryVolumeFileIdentifierMaxLength - (1 + len(version))
|
||||
if len(extension) > 0 {
|
||||
maxRemainingFilenameLength -= (1 + len(extension))
|
||||
}
|
||||
|
||||
filename = mangleD1String(filename, maxRemainingFilenameLength)
|
||||
|
||||
if len(extension) > 0 {
|
||||
return filename + "." + extension + ";" + version
|
||||
}
|
||||
|
||||
return filename + ";" + version
|
||||
}
|
||||
|
||||
// See ECMA-119 7.6
|
||||
func mangleDirectoryName(input string) string {
|
||||
return mangleD1String(input, primaryVolumeDirectoryIdentifierMaxLength)
|
||||
}
|
||||
|
||||
func mangleD1String(input string, maxCharacters int) string {
|
||||
// https://github.com/torvalds/linux/blob/v5.6/fs/isofs/dir.c#L29
|
||||
input = strings.ToLower(input)
|
||||
|
||||
var mangledString string
|
||||
for i := 0; i < len(input) && i < maxCharacters; i++ {
|
||||
r := rune(input[i])
|
||||
if strings.ContainsRune(d1Characters, r) {
|
||||
mangledString += string(r)
|
||||
} else {
|
||||
mangledString += "_"
|
||||
}
|
||||
}
|
||||
|
||||
return mangledString
|
||||
}
|
||||
|
||||
// calculateDirChildrenSectors calculates the total mashalled size of all DirectoryEntries
|
||||
// within a directory. The size of each entry depends of the length of the filename.
|
||||
func calculateDirChildrenSectors(path string) (uint32, error) {
|
||||
contents, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var sectors uint32
|
||||
var currentSectorOccupied uint32 = 68 // the 0x00 and 0x01 entries
|
||||
|
||||
for _, c := range contents {
|
||||
identifierLen := len(c.Name())
|
||||
idPaddingLen := (identifierLen + 1) % 2
|
||||
entryLength := uint32(33 + identifierLen + idPaddingLen)
|
||||
|
||||
if currentSectorOccupied+entryLength > sectorSize {
|
||||
sectors++
|
||||
currentSectorOccupied = entryLength
|
||||
} else {
|
||||
currentSectorOccupied += entryLength
|
||||
}
|
||||
}
|
||||
|
||||
if currentSectorOccupied > 0 {
|
||||
sectors++
|
||||
}
|
||||
|
||||
return sectors, nil
|
||||
}
|
||||
|
||||
func fileLengthToSectors(l uint32) uint32 {
|
||||
if (l % sectorSize) == 0 {
|
||||
return l / sectorSize
|
||||
}
|
||||
|
||||
return (l / sectorSize) + 1
|
||||
}
|
||||
|
||||
type writeContext struct {
|
||||
stagingDir string
|
||||
timestamp RecordingTimestamp
|
||||
freeSectorPointer uint32
|
||||
}
|
||||
|
||||
func (wc *writeContext) allocateSectors(n uint32) uint32 {
|
||||
return atomic.AddUint32(&wc.freeSectorPointer, n) - n
|
||||
}
|
||||
|
||||
func (wc *writeContext) createDEForRoot() (*DirectoryEntry, error) {
|
||||
extentLengthInSectors, err := calculateDirChildrenSectors(wc.stagingDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extentLocation := wc.allocateSectors(extentLengthInSectors)
|
||||
de := &DirectoryEntry{
|
||||
ExtendedAtributeRecordLength: 0,
|
||||
ExtentLocation: int32(extentLocation),
|
||||
ExtentLength: uint32(extentLengthInSectors * sectorSize),
|
||||
RecordingDateTime: wc.timestamp,
|
||||
FileFlags: dirFlagDir,
|
||||
FileUnitSize: 0, // 0 for non-interleaved write
|
||||
InterleaveGap: 0, // not interleaved
|
||||
VolumeSequenceNumber: 1, // we only have one volume
|
||||
Identifier: string([]byte{0}),
|
||||
SystemUse: []byte{},
|
||||
}
|
||||
return de, nil
|
||||
}
|
||||
|
||||
type itemToWrite struct {
|
||||
isDirectory bool
|
||||
dirPath string
|
||||
ownEntry *DirectoryEntry
|
||||
parentEntery *DirectoryEntry
|
||||
childrenEntries []*DirectoryEntry
|
||||
targetSector uint32
|
||||
}
|
||||
|
||||
// scanDirectory reads the directory's contents and adds them to the queue, as well as stores all their DirectoryEntries in the item,
|
||||
// because we'll need them to write this item's descriptor.
|
||||
func (wc *writeContext) scanDirectory(item *itemToWrite, dirPath string, ownEntry *DirectoryEntry, parentEntery *DirectoryEntry, targetSector uint32) (*list.List, error) {
|
||||
contents, err := os.ReadDir(dirPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
itemsToWrite := list.New()
|
||||
|
||||
for _, c := range contents {
|
||||
var (
|
||||
fileFlags byte
|
||||
extentLengthInSectors uint32
|
||||
extentLength uint32
|
||||
)
|
||||
if c.IsDir() {
|
||||
extentLengthInSectors, err = calculateDirChildrenSectors(path.Join(dirPath, c.Name()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fileFlags = dirFlagDir
|
||||
extentLength = extentLengthInSectors * sectorSize
|
||||
} else {
|
||||
fileinfo, err := c.Info()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fileinfo.Size() > int64(math.MaxUint32) {
|
||||
return nil, ErrFileTooLarge
|
||||
}
|
||||
extentLength = uint32(fileinfo.Size())
|
||||
extentLengthInSectors = fileLengthToSectors(extentLength)
|
||||
|
||||
fileFlags = 0
|
||||
}
|
||||
|
||||
extentLocation := wc.allocateSectors(extentLengthInSectors)
|
||||
de := &DirectoryEntry{
|
||||
ExtendedAtributeRecordLength: 0,
|
||||
ExtentLocation: int32(extentLocation),
|
||||
ExtentLength: uint32(extentLength),
|
||||
RecordingDateTime: wc.timestamp,
|
||||
FileFlags: fileFlags,
|
||||
FileUnitSize: 0, // 0 for non-interleaved write
|
||||
InterleaveGap: 0, // not interleaved
|
||||
VolumeSequenceNumber: 1, // we only have one volume
|
||||
Identifier: c.Name(),
|
||||
SystemUse: []byte{},
|
||||
}
|
||||
|
||||
// Add this child's descriptor to the currently scanned directory's list of children,
|
||||
// so that later we can use it for writing the current item.
|
||||
if item.childrenEntries == nil {
|
||||
item.childrenEntries = []*DirectoryEntry{de}
|
||||
} else {
|
||||
item.childrenEntries = append(item.childrenEntries, de)
|
||||
}
|
||||
|
||||
// queue this child for processing
|
||||
itemsToWrite.PushBack(itemToWrite{
|
||||
isDirectory: c.IsDir(),
|
||||
dirPath: path.Join(dirPath, c.Name()),
|
||||
ownEntry: de,
|
||||
parentEntery: ownEntry,
|
||||
targetSector: uint32(de.ExtentLocation),
|
||||
})
|
||||
}
|
||||
|
||||
return itemsToWrite, nil
|
||||
}
|
||||
|
||||
// processDirectory writes a given directory item to the destination sectors
|
||||
func processDirectory(w io.Writer, children []*DirectoryEntry, ownEntry *DirectoryEntry, parentEntry *DirectoryEntry) error {
|
||||
var currentOffset uint32
|
||||
|
||||
currentDE := ownEntry.Clone()
|
||||
currentDE.Identifier = string([]byte{0})
|
||||
parentDE := parentEntry.Clone()
|
||||
parentDE.Identifier = string([]byte{1})
|
||||
|
||||
currentDEData, err := currentDE.MarshalBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parentDEData, err := parentDE.MarshalBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n, err := w.Write(currentDEData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentOffset += uint32(n)
|
||||
n, err = w.Write(parentDEData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentOffset += uint32(n)
|
||||
|
||||
for _, childDescriptor := range children {
|
||||
data, err := childDescriptor.MarshalBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
remainingSectorSpace := sectorSize - (currentOffset % sectorSize)
|
||||
if remainingSectorSpace < uint32(len(data)) {
|
||||
// ECMA-119 6.8.1.1 If the body of the next descriptor won't fit into the sector,
|
||||
// we fill the rest of space with zeros and skip to the next sector.
|
||||
zeros := bytes.Repeat([]byte{0}, int(remainingSectorSpace))
|
||||
_, err = w.Write(zeros)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// skip to the next sector
|
||||
currentOffset = 0
|
||||
}
|
||||
|
||||
n, err = w.Write(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentOffset += uint32(n)
|
||||
}
|
||||
|
||||
// fill with zeros to the end of the sector
|
||||
remainingSectorSpace := sectorSize - (currentOffset % sectorSize)
|
||||
if remainingSectorSpace != 0 {
|
||||
zeros := bytes.Repeat([]byte{0}, int(remainingSectorSpace))
|
||||
_, err = w.Write(zeros)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func processFile(w io.Writer, dirPath string) error {
|
||||
f, err := os.Open(dirPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fileinfo, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if fileinfo.Size() > int64(math.MaxUint32) {
|
||||
return ErrFileTooLarge
|
||||
}
|
||||
|
||||
buffer := make([]byte, sectorSize)
|
||||
|
||||
for bytesLeft := uint32(fileinfo.Size()); bytesLeft > 0; {
|
||||
var toRead uint32
|
||||
if bytesLeft < sectorSize {
|
||||
toRead = bytesLeft
|
||||
} else {
|
||||
toRead = sectorSize
|
||||
}
|
||||
|
||||
if _, err = io.ReadAtLeast(f, buffer, int(toRead)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = w.Write(buffer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bytesLeft -= toRead
|
||||
}
|
||||
// We already write a whole sector-sized buffer, so there's need to fill with zeroes.
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// traverseStagingDir creates a new queue of items to write by traversing the staging directory
|
||||
func (wc *writeContext) traverseStagingDir(rootItem itemToWrite) (*list.List, error) {
|
||||
itemsToWrite := list.New()
|
||||
itemsToWrite.PushBack(rootItem)
|
||||
|
||||
for item := itemsToWrite.Front(); item != nil; item = item.Next() {
|
||||
it := item.Value.(itemToWrite)
|
||||
|
||||
if it.isDirectory {
|
||||
newItems, err := wc.scanDirectory(&it, it.dirPath, it.ownEntry, it.parentEntery, it.targetSector)
|
||||
if err != nil {
|
||||
relativePath := it.dirPath[len(wc.stagingDir):]
|
||||
return nil, fmt.Errorf("processing %s: %s", relativePath, err)
|
||||
}
|
||||
itemsToWrite.PushBackList(newItems)
|
||||
}
|
||||
|
||||
item.Value = it
|
||||
}
|
||||
|
||||
return itemsToWrite, nil
|
||||
}
|
||||
|
||||
func writeAll(w io.Writer, itemsToWrite *list.List) error {
|
||||
for item := itemsToWrite.Front(); item != nil; item = item.Next() {
|
||||
it := item.Value.(itemToWrite)
|
||||
var err error
|
||||
if it.isDirectory {
|
||||
err = processDirectory(w, it.childrenEntries, it.ownEntry, it.parentEntery)
|
||||
} else {
|
||||
err = processFile(w, it.dirPath)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteTo writes the image to the given WriterAt
|
||||
func (iw *ImageWriter) WriteTo(w io.Writer, volumeIdentifier string) error {
|
||||
now := time.Now()
|
||||
|
||||
wc := writeContext{
|
||||
stagingDir: iw.stagingDir,
|
||||
timestamp: RecordingTimestamp{},
|
||||
freeSectorPointer: 18, // system area (16) + 2 volume descriptors
|
||||
}
|
||||
|
||||
rootDE, err := wc.createDEForRoot()
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating root directory descriptor: %s", err)
|
||||
}
|
||||
|
||||
rootItem := itemToWrite{
|
||||
isDirectory: true,
|
||||
dirPath: wc.stagingDir,
|
||||
ownEntry: rootDE,
|
||||
parentEntery: rootDE,
|
||||
targetSector: uint32(rootDE.ExtentLocation),
|
||||
}
|
||||
|
||||
itemsToWrite, err := wc.traverseStagingDir(rootItem)
|
||||
if err != nil {
|
||||
return fmt.Errorf("tranversing staging directory: %s", err)
|
||||
}
|
||||
|
||||
pvd := volumeDescriptor{
|
||||
Header: volumeDescriptorHeader{
|
||||
Type: volumeTypePrimary,
|
||||
Identifier: standardIdentifierBytes,
|
||||
Version: 1,
|
||||
},
|
||||
Primary: &PrimaryVolumeDescriptorBody{
|
||||
SystemIdentifier: runtime.GOOS,
|
||||
VolumeIdentifier: volumeIdentifier,
|
||||
VolumeSpaceSize: int32(wc.freeSectorPointer),
|
||||
VolumeSetSize: 1,
|
||||
VolumeSequenceNumber: 1,
|
||||
LogicalBlockSize: int16(sectorSize),
|
||||
PathTableSize: 0,
|
||||
TypeLPathTableLoc: 0,
|
||||
OptTypeLPathTableLoc: 0,
|
||||
TypeMPathTableLoc: 0,
|
||||
OptTypeMPathTableLoc: 0,
|
||||
RootDirectoryEntry: rootDE,
|
||||
VolumeSetIdentifier: "",
|
||||
PublisherIdentifier: "",
|
||||
DataPreparerIdentifier: "",
|
||||
ApplicationIdentifier: "github.com/kdomanski/iso9660",
|
||||
CopyrightFileIdentifier: "",
|
||||
AbstractFileIdentifier: "",
|
||||
BibliographicFileIdentifier: "",
|
||||
VolumeCreationDateAndTime: VolumeDescriptorTimestampFromTime(now),
|
||||
VolumeModificationDateAndTime: VolumeDescriptorTimestampFromTime(now),
|
||||
VolumeExpirationDateAndTime: VolumeDescriptorTimestamp{},
|
||||
VolumeEffectiveDateAndTime: VolumeDescriptorTimestampFromTime(now),
|
||||
FileStructureVersion: 1,
|
||||
ApplicationUsed: [512]byte{},
|
||||
},
|
||||
}
|
||||
|
||||
terminator := volumeDescriptor{
|
||||
Header: volumeDescriptorHeader{
|
||||
Type: volumeTypeTerminator,
|
||||
Identifier: standardIdentifierBytes,
|
||||
Version: 1,
|
||||
},
|
||||
}
|
||||
|
||||
// write 16 sectors of zeroes
|
||||
zeroSector := bytes.Repeat([]byte{0}, int(sectorSize))
|
||||
for i := uint32(0); i < 16; i++ {
|
||||
if _, err = w.Write(zeroSector); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
buffer, err := pvd.MarshalBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = w.Write(buffer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if buffer, err = terminator.MarshalBinary(); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = w.Write(buffer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = writeAll(w, itemsToWrite); err != nil {
|
||||
return fmt.Errorf("writing files: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+605
@@ -0,0 +1,605 @@
|
||||
// Package iso9660 implements reading and creating basic ISO9660 images.
|
||||
package iso9660
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ISO 9660 Overview
|
||||
// https://archive.fo/xs9ac
|
||||
|
||||
const (
|
||||
sectorSize uint32 = 2048
|
||||
systemAreaSize = sectorSize * 16
|
||||
standardIdentifier = "CD001"
|
||||
udfIdentifier = "BEA01"
|
||||
|
||||
volumeTypeBoot byte = 0
|
||||
volumeTypePrimary byte = 1
|
||||
volumeTypeSupplementary byte = 2
|
||||
volumeTypePartition byte = 3
|
||||
volumeTypeTerminator byte = 255
|
||||
|
||||
volumeDescriptorBodySize = sectorSize - 7
|
||||
|
||||
aCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_!\"%&'()*+,-./:;<=>?"
|
||||
dCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
|
||||
// ECMA-119 7.4.2.2 defines d1-characters as
|
||||
// "subject to agreement between the originator and the recipient of the volume".
|
||||
d1Characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_!\"%&'()*+,-./:;<=>?"
|
||||
)
|
||||
|
||||
const (
|
||||
dirFlagHidden = 1 << iota
|
||||
dirFlagDir
|
||||
dirFlagAssociated
|
||||
dirFlagRecord
|
||||
dirFlagProtection
|
||||
_
|
||||
_
|
||||
dirFlagMultiExtent
|
||||
)
|
||||
|
||||
var standardIdentifierBytes = [5]byte{'C', 'D', '0', '0', '1'}
|
||||
|
||||
var ErrUDFNotSupported = errors.New("UDF volumes are not supported")
|
||||
|
||||
// volumeDescriptorHeader represents the data in bytes 0-6
|
||||
// of a Volume Descriptor as defined in ECMA-119 8.1
|
||||
type volumeDescriptorHeader struct {
|
||||
Type byte
|
||||
Identifier [5]byte
|
||||
Version byte
|
||||
}
|
||||
|
||||
var _ encoding.BinaryUnmarshaler = &volumeDescriptorHeader{}
|
||||
var _ encoding.BinaryMarshaler = &volumeDescriptorHeader{}
|
||||
|
||||
// UnmarshalBinary decodes a volumeDescriptorHeader from binary form
|
||||
func (vdh *volumeDescriptorHeader) UnmarshalBinary(data []byte) error {
|
||||
if len(data) < 7 {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
vdh.Type = data[0]
|
||||
copy(vdh.Identifier[:], data[1:6])
|
||||
vdh.Version = data[6]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vdh volumeDescriptorHeader) MarshalBinary() ([]byte, error) {
|
||||
data := make([]byte, 7)
|
||||
data[0] = vdh.Type
|
||||
data[6] = vdh.Version
|
||||
copy(data[1:6], vdh.Identifier[:])
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// BootVolumeDescriptorBody represents the data in bytes 7-2047
|
||||
// of a Boot Record as defined in ECMA-119 8.2
|
||||
type BootVolumeDescriptorBody struct {
|
||||
BootSystemIdentifier string
|
||||
BootIdentifier string
|
||||
BootSystemUse [1977]byte
|
||||
}
|
||||
|
||||
var _ encoding.BinaryUnmarshaler = &BootVolumeDescriptorBody{}
|
||||
|
||||
// PrimaryVolumeDescriptorBody represents the data in bytes 7-2047
|
||||
// of a Primary Volume Descriptor as defined in ECMA-119 8.4
|
||||
type PrimaryVolumeDescriptorBody struct {
|
||||
SystemIdentifier string
|
||||
VolumeIdentifier string
|
||||
VolumeSpaceSize int32
|
||||
VolumeSetSize int16
|
||||
VolumeSequenceNumber int16
|
||||
LogicalBlockSize int16
|
||||
PathTableSize int32
|
||||
TypeLPathTableLoc int32
|
||||
OptTypeLPathTableLoc int32
|
||||
TypeMPathTableLoc int32
|
||||
OptTypeMPathTableLoc int32
|
||||
RootDirectoryEntry *DirectoryEntry
|
||||
VolumeSetIdentifier string
|
||||
PublisherIdentifier string
|
||||
DataPreparerIdentifier string
|
||||
ApplicationIdentifier string
|
||||
CopyrightFileIdentifier string
|
||||
AbstractFileIdentifier string
|
||||
BibliographicFileIdentifier string
|
||||
VolumeCreationDateAndTime VolumeDescriptorTimestamp
|
||||
VolumeModificationDateAndTime VolumeDescriptorTimestamp
|
||||
VolumeExpirationDateAndTime VolumeDescriptorTimestamp
|
||||
VolumeEffectiveDateAndTime VolumeDescriptorTimestamp
|
||||
FileStructureVersion byte
|
||||
ApplicationUsed [512]byte
|
||||
}
|
||||
|
||||
var _ encoding.BinaryUnmarshaler = &PrimaryVolumeDescriptorBody{}
|
||||
var _ encoding.BinaryMarshaler = PrimaryVolumeDescriptorBody{}
|
||||
|
||||
// DirectoryEntry contains data from a Directory Descriptor
|
||||
// as described by ECMA-119 9.1
|
||||
type DirectoryEntry struct {
|
||||
ExtendedAtributeRecordLength byte
|
||||
ExtentLocation int32
|
||||
ExtentLength uint32
|
||||
RecordingDateTime RecordingTimestamp
|
||||
FileFlags byte
|
||||
FileUnitSize byte
|
||||
InterleaveGap byte
|
||||
VolumeSequenceNumber int16
|
||||
Identifier string
|
||||
SystemUse []byte
|
||||
SystemUseEntries SystemUseEntrySlice
|
||||
}
|
||||
|
||||
var _ encoding.BinaryUnmarshaler = &DirectoryEntry{}
|
||||
var _ encoding.BinaryMarshaler = &DirectoryEntry{}
|
||||
|
||||
// UnmarshalBinary decodes a DirectoryEntry from binary form
|
||||
func (de *DirectoryEntry) UnmarshalBinary(data []byte) error {
|
||||
length := data[0]
|
||||
if length == 0 {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
de.ExtendedAtributeRecordLength = data[1]
|
||||
|
||||
if de.ExtentLocation, err = UnmarshalInt32LSBMSB(data[2:10]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if de.ExtentLength, err = UnmarshalUint32LSBMSB(data[10:18]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = de.RecordingDateTime.UnmarshalBinary(data[18:25]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
de.FileFlags = data[25]
|
||||
de.FileUnitSize = data[26]
|
||||
de.InterleaveGap = data[27]
|
||||
|
||||
if de.VolumeSequenceNumber, err = UnmarshalInt16LSBMSB(data[28:32]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
identifierLen := data[32]
|
||||
de.Identifier = string(data[33 : 33+identifierLen])
|
||||
|
||||
// add padding if identifier length was even]
|
||||
idPaddingLen := (identifierLen + 1) % 2
|
||||
de.SystemUse = data[33+identifierLen+idPaddingLen : length]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary encodes a DirectoryEntry to binary form
|
||||
func (de *DirectoryEntry) MarshalBinary() ([]byte, error) {
|
||||
identifierLen := len(de.Identifier)
|
||||
idPaddingLen := (identifierLen + 1) % 2
|
||||
totalLen := 33 + identifierLen + idPaddingLen + len(de.SystemUse)
|
||||
if totalLen > 255 {
|
||||
return nil, fmt.Errorf("identifier %q is too long", de.Identifier)
|
||||
}
|
||||
|
||||
data := make([]byte, totalLen)
|
||||
|
||||
data[0] = byte(totalLen)
|
||||
data[1] = de.ExtendedAtributeRecordLength
|
||||
|
||||
WriteInt32LSBMSB(data[2:10], de.ExtentLocation)
|
||||
WriteInt32LSBMSB(data[10:18], int32(de.ExtentLength))
|
||||
de.RecordingDateTime.MarshalBinary(data[18:25])
|
||||
data[25] = de.FileFlags
|
||||
data[26] = de.FileUnitSize
|
||||
data[27] = de.InterleaveGap
|
||||
WriteInt16LSBMSB(data[28:32], de.VolumeSequenceNumber)
|
||||
data[32] = byte(identifierLen)
|
||||
copy(data[33:33+identifierLen], []byte(de.Identifier))
|
||||
|
||||
copy(data[33+identifierLen+idPaddingLen:totalLen], de.SystemUse)
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Clone creates a copy of the DirectoryEntry
|
||||
func (de *DirectoryEntry) Clone() DirectoryEntry {
|
||||
newDE := DirectoryEntry{
|
||||
ExtendedAtributeRecordLength: de.ExtendedAtributeRecordLength,
|
||||
ExtentLocation: de.ExtentLocation,
|
||||
ExtentLength: de.ExtentLength,
|
||||
RecordingDateTime: de.RecordingDateTime,
|
||||
FileFlags: de.FileFlags,
|
||||
FileUnitSize: de.FileUnitSize,
|
||||
InterleaveGap: de.InterleaveGap,
|
||||
VolumeSequenceNumber: de.VolumeSequenceNumber,
|
||||
Identifier: de.Identifier,
|
||||
SystemUse: make([]byte, len(de.SystemUse)),
|
||||
}
|
||||
copy(newDE.SystemUse, de.SystemUse)
|
||||
return newDE
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes a PrimaryVolumeDescriptorBody from binary form as defined in ECMA-119 8.4
|
||||
func (pvd *PrimaryVolumeDescriptorBody) UnmarshalBinary(data []byte) error {
|
||||
if len(data) < 2048 {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
pvd.SystemIdentifier = strings.TrimRight(string(data[8:40]), " ")
|
||||
pvd.VolumeIdentifier = strings.TrimRight(string(data[40:72]), " ")
|
||||
|
||||
if pvd.VolumeSpaceSize, err = UnmarshalInt32LSBMSB(data[80:88]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if pvd.VolumeSetSize, err = UnmarshalInt16LSBMSB(data[120:124]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if pvd.VolumeSequenceNumber, err = UnmarshalInt16LSBMSB(data[124:128]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if pvd.LogicalBlockSize, err = UnmarshalInt16LSBMSB(data[128:132]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if pvd.PathTableSize, err = UnmarshalInt32LSBMSB(data[132:140]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pvd.TypeLPathTableLoc = int32(binary.LittleEndian.Uint32(data[140:144]))
|
||||
pvd.OptTypeLPathTableLoc = int32(binary.LittleEndian.Uint32(data[144:148]))
|
||||
pvd.TypeMPathTableLoc = int32(binary.BigEndian.Uint32(data[148:152]))
|
||||
pvd.OptTypeMPathTableLoc = int32(binary.BigEndian.Uint32(data[152:156]))
|
||||
|
||||
if pvd.RootDirectoryEntry == nil {
|
||||
pvd.RootDirectoryEntry = &DirectoryEntry{}
|
||||
}
|
||||
if err = pvd.RootDirectoryEntry.UnmarshalBinary(data[156:190]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pvd.VolumeSetIdentifier = strings.TrimRight(string(data[190:318]), " ")
|
||||
pvd.PublisherIdentifier = strings.TrimRight(string(data[318:446]), " ")
|
||||
pvd.DataPreparerIdentifier = strings.TrimRight(string(data[446:574]), " ")
|
||||
pvd.ApplicationIdentifier = strings.TrimRight(string(data[574:702]), " ")
|
||||
pvd.CopyrightFileIdentifier = strings.TrimRight(string(data[702:740]), " ")
|
||||
pvd.AbstractFileIdentifier = strings.TrimRight(string(data[740:776]), " ")
|
||||
pvd.BibliographicFileIdentifier = strings.TrimRight(string(data[776:813]), " ")
|
||||
|
||||
if pvd.VolumeCreationDateAndTime.UnmarshalBinary(data[813:830]) != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if pvd.VolumeModificationDateAndTime.UnmarshalBinary(data[830:847]) != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if pvd.VolumeExpirationDateAndTime.UnmarshalBinary(data[847:864]) != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if pvd.VolumeEffectiveDateAndTime.UnmarshalBinary(data[864:881]) != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pvd.FileStructureVersion = data[881]
|
||||
copy(pvd.ApplicationUsed[:], data[883:1395])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary encodes the PrimaryVolumeDescriptorBody to its binary form
|
||||
func (pvd PrimaryVolumeDescriptorBody) MarshalBinary() ([]byte, error) {
|
||||
output := make([]byte, sectorSize)
|
||||
|
||||
d := MarshalString(pvd.SystemIdentifier, 32)
|
||||
copy(output[8:40], d)
|
||||
|
||||
d = MarshalString(pvd.VolumeIdentifier, 32)
|
||||
copy(output[40:72], d)
|
||||
|
||||
WriteInt32LSBMSB(output[80:88], pvd.VolumeSpaceSize)
|
||||
WriteInt16LSBMSB(output[120:124], pvd.VolumeSetSize)
|
||||
WriteInt16LSBMSB(output[124:128], pvd.VolumeSequenceNumber)
|
||||
WriteInt16LSBMSB(output[128:132], pvd.LogicalBlockSize)
|
||||
WriteInt32LSBMSB(output[132:140], pvd.PathTableSize)
|
||||
|
||||
binary.LittleEndian.PutUint32(output[140:144], uint32(pvd.TypeLPathTableLoc))
|
||||
binary.LittleEndian.PutUint32(output[144:148], uint32(pvd.OptTypeLPathTableLoc))
|
||||
binary.BigEndian.PutUint32(output[148:152], uint32(pvd.TypeMPathTableLoc))
|
||||
binary.BigEndian.PutUint32(output[152:156], uint32(pvd.OptTypeMPathTableLoc))
|
||||
|
||||
binaryRDE, err := pvd.RootDirectoryEntry.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(output[156:190], binaryRDE)
|
||||
|
||||
copy(output[190:318], MarshalString(pvd.VolumeSetIdentifier, 128))
|
||||
copy(output[318:446], MarshalString(pvd.PublisherIdentifier, 128))
|
||||
copy(output[446:574], MarshalString(pvd.DataPreparerIdentifier, 128))
|
||||
copy(output[574:702], MarshalString(pvd.ApplicationIdentifier, 128))
|
||||
copy(output[702:740], MarshalString(pvd.CopyrightFileIdentifier, 38))
|
||||
copy(output[740:776], MarshalString(pvd.AbstractFileIdentifier, 36))
|
||||
copy(output[776:813], MarshalString(pvd.BibliographicFileIdentifier, 37))
|
||||
|
||||
d, err = pvd.VolumeCreationDateAndTime.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(output[813:830], d)
|
||||
|
||||
d, err = pvd.VolumeModificationDateAndTime.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(output[830:847], d)
|
||||
|
||||
d, err = pvd.VolumeExpirationDateAndTime.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(output[847:864], d)
|
||||
|
||||
d, err = pvd.VolumeEffectiveDateAndTime.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(output[864:881], d)
|
||||
|
||||
output[881] = pvd.FileStructureVersion
|
||||
output[882] = 0
|
||||
copy(output[883:1395], pvd.ApplicationUsed[:])
|
||||
for i := 1395; i < 2048; i++ {
|
||||
output[i] = 0
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes a BootVolumeDescriptorBody from binary form
|
||||
func (bvd *BootVolumeDescriptorBody) UnmarshalBinary(data []byte) error {
|
||||
bvd.BootSystemIdentifier = strings.TrimRight(string(data[7:39]), " ")
|
||||
bvd.BootIdentifier = strings.TrimRight(string(data[39:71]), " ")
|
||||
if n := copy(bvd.BootSystemUse[:], data[71:2048]); n != 1977 {
|
||||
return fmt.Errorf("BootVolumeDescriptorBody.UnmarshalBinary: copied %d bytes", n)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type volumeDescriptor struct {
|
||||
Header volumeDescriptorHeader
|
||||
Boot *BootVolumeDescriptorBody
|
||||
Primary *PrimaryVolumeDescriptorBody
|
||||
}
|
||||
|
||||
var _ encoding.BinaryUnmarshaler = &volumeDescriptor{}
|
||||
var _ encoding.BinaryMarshaler = &volumeDescriptor{}
|
||||
|
||||
func (vd volumeDescriptor) Type() byte {
|
||||
return vd.Header.Type
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes a volumeDescriptor from binary form
|
||||
func (vd *volumeDescriptor) UnmarshalBinary(data []byte) error {
|
||||
if uint32(len(data)) < sectorSize {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
if err := vd.Header.UnmarshalBinary(data); err != nil {
|
||||
// this should never fail, since volumeDescriptorHeader.UnmarshalBinary( ) only checks data size too
|
||||
return err
|
||||
}
|
||||
|
||||
id := string(vd.Header.Identifier[:])
|
||||
if id != standardIdentifier {
|
||||
if id == udfIdentifier {
|
||||
return ErrUDFNotSupported
|
||||
}
|
||||
return fmt.Errorf("volume descriptor %q != %q", id, standardIdentifier)
|
||||
}
|
||||
|
||||
switch vd.Header.Type {
|
||||
case volumeTypeBoot:
|
||||
vd.Boot = &BootVolumeDescriptorBody{}
|
||||
return vd.Boot.UnmarshalBinary(data)
|
||||
case volumeTypePartition:
|
||||
return errors.New("partition volumes are not yet supported")
|
||||
case volumeTypePrimary, volumeTypeSupplementary:
|
||||
vd.Primary = &PrimaryVolumeDescriptorBody{}
|
||||
return vd.Primary.UnmarshalBinary(data)
|
||||
case volumeTypeTerminator:
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("unknown volume type 0x%X", vd.Header.Type)
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes a volumeDescriptor from binary form
|
||||
func (vd volumeDescriptor) MarshalBinary() ([]byte, error) {
|
||||
var output []byte
|
||||
var err error
|
||||
|
||||
switch vd.Header.Type {
|
||||
case volumeTypeBoot:
|
||||
return nil, errors.New("boot volumes are not yet supported")
|
||||
case volumeTypePartition:
|
||||
return nil, errors.New("partition volumes are not yet supported")
|
||||
case volumeTypePrimary, volumeTypeSupplementary:
|
||||
if output, err = vd.Primary.MarshalBinary(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case volumeTypeTerminator:
|
||||
output = make([]byte, sectorSize)
|
||||
}
|
||||
|
||||
data, err := vd.Header.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
copy(output[0:7], data)
|
||||
|
||||
return output, nil
|
||||
}
|
||||
|
||||
// VolumeDescriptorTimestamp represents a time and date format
|
||||
// that can be encoded according to ECMA-119 8.4.26.1
|
||||
type VolumeDescriptorTimestamp struct {
|
||||
Year int
|
||||
Month int
|
||||
Day int
|
||||
Hour int
|
||||
Minute int
|
||||
Second int
|
||||
Hundredth int
|
||||
Offset int
|
||||
}
|
||||
|
||||
var _ encoding.BinaryMarshaler = &VolumeDescriptorTimestamp{}
|
||||
var _ encoding.BinaryUnmarshaler = &VolumeDescriptorTimestamp{}
|
||||
|
||||
// MarshalBinary encodes the timestamp into a binary form
|
||||
func (ts *VolumeDescriptorTimestamp) MarshalBinary() ([]byte, error) {
|
||||
formatted := fmt.Sprintf("%04d%02d%02d%02d%02d%02d%02d", ts.Year, ts.Month, ts.Day, ts.Hour, ts.Minute, ts.Second, ts.Hundredth)
|
||||
formattedBytes := append([]byte(formatted), byte(ts.Offset))
|
||||
if len(formattedBytes) != 17 {
|
||||
return nil, fmt.Errorf("VolumeDescriptorTimestamp.MarshalBinary: the formatted timestamp is %d bytes long", len(formatted))
|
||||
}
|
||||
return formattedBytes, nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes a VolumeDescriptorTimestamp from binary form
|
||||
func (ts *VolumeDescriptorTimestamp) UnmarshalBinary(data []byte) error {
|
||||
if len(data) < 17 {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
year, err := strconv.Atoi(strings.TrimSpace(string(data[0:4])))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
month, err := strconv.Atoi(strings.TrimSpace(string(data[4:6])))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
day, err := strconv.Atoi(strings.TrimSpace(string(data[6:8])))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hour, err := strconv.Atoi(strings.TrimSpace(string(data[8:10])))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
min, err := strconv.Atoi(strings.TrimSpace(string(data[10:12])))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sec, err := strconv.Atoi(strings.TrimSpace(string(data[12:14])))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hundredth, err := strconv.Atoi(strings.TrimSpace(string(data[14:16])))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*ts = VolumeDescriptorTimestamp{
|
||||
Year: year,
|
||||
Month: month,
|
||||
Day: day,
|
||||
Hour: hour,
|
||||
Minute: min,
|
||||
Second: sec,
|
||||
Hundredth: hundredth,
|
||||
Offset: int(data[16]),
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RecordingTimestamp represents a time and date format
|
||||
// that can be encoded according to ECMA-119 9.1.5
|
||||
type RecordingTimestamp time.Time
|
||||
|
||||
var _ encoding.BinaryUnmarshaler = &RecordingTimestamp{}
|
||||
|
||||
// UnmarshalBinary decodes a RecordingTimestamp from binary form
|
||||
func (ts *RecordingTimestamp) UnmarshalBinary(data []byte) error {
|
||||
if len(data) < 7 {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
year := 1900 + int(data[0])
|
||||
month := int(data[1])
|
||||
day := int(data[2])
|
||||
hour := int(data[3])
|
||||
min := int(data[4])
|
||||
sec := int(data[5])
|
||||
tzOffset := int(data[6])
|
||||
secondsInAQuarter := 60 * 15
|
||||
|
||||
tz := time.FixedZone("", tzOffset*secondsInAQuarter)
|
||||
*ts = RecordingTimestamp(time.Date(year, time.Month(month), day, hour, min, sec, 0, tz))
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary encodes the RecordingTimestamp in its binary form to a buffer
|
||||
// of the length of 7 or more bytes
|
||||
func (ts RecordingTimestamp) MarshalBinary(dst []byte) {
|
||||
_ = dst[6] // early bounds check to guarantee safety of writes below
|
||||
t := time.Time(ts)
|
||||
year, month, day := t.Date()
|
||||
hour, min, sec := t.Clock()
|
||||
_, secOffset := t.Zone()
|
||||
secondsInAQuarter := 60 * 15
|
||||
offsetInQuarters := secOffset / secondsInAQuarter
|
||||
dst[0] = byte(year - 1900)
|
||||
dst[1] = byte(month)
|
||||
dst[2] = byte(day)
|
||||
dst[3] = byte(hour)
|
||||
dst[4] = byte(min)
|
||||
dst[5] = byte(sec)
|
||||
dst[6] = byte(offsetInQuarters)
|
||||
}
|
||||
|
||||
// VolumeDescriptorTimestampFromTime converts time.Time to VolumeDescriptorTimestamp
|
||||
func VolumeDescriptorTimestampFromTime(t time.Time) VolumeDescriptorTimestamp {
|
||||
t = t.UTC()
|
||||
year, month, day := t.Date()
|
||||
hour, minute, second := t.Clock()
|
||||
hundredth := t.Nanosecond() / 10000000
|
||||
return VolumeDescriptorTimestamp{
|
||||
Year: year,
|
||||
Month: int(month),
|
||||
Day: day,
|
||||
Hour: hour,
|
||||
Minute: minute,
|
||||
Second: second,
|
||||
Hundredth: hundredth,
|
||||
Offset: 0, // we converted to UTC
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package iso9660
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MarshalString encodes the given string as a byte array padded to the given length
|
||||
func MarshalString(s string, padToLength int) []byte {
|
||||
if len(s) > padToLength {
|
||||
s = s[:padToLength]
|
||||
}
|
||||
missingPadding := padToLength - len(s)
|
||||
s = s + strings.Repeat(" ", missingPadding)
|
||||
return []byte(s)
|
||||
}
|
||||
|
||||
// UnmarshalInt32LSBMSB decodes a 32-bit integer in both byte orders, as defined in ECMA-119 7.3.3
|
||||
func UnmarshalInt32LSBMSB(data []byte) (int32, error) {
|
||||
if len(data) < 8 {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
lsb := int32(binary.LittleEndian.Uint32(data[0:4]))
|
||||
msb := int32(binary.BigEndian.Uint32(data[4:8]))
|
||||
|
||||
if lsb != msb {
|
||||
return 0, fmt.Errorf("little-endian and big-endian value mismatch: %d != %d", lsb, msb)
|
||||
}
|
||||
|
||||
return lsb, nil
|
||||
}
|
||||
|
||||
// UnmarshalUint32LSBMSB is the same as UnmarshalInt32LSBMSB but returns an unsigned integer
|
||||
func UnmarshalUint32LSBMSB(data []byte) (uint32, error) {
|
||||
n, err := UnmarshalInt32LSBMSB(data)
|
||||
return uint32(n), err
|
||||
}
|
||||
|
||||
// UnmarshalInt16LSBMSB decodes a 16-bit integer in both byte orders, as defined in ECMA-119 7.3.3
|
||||
func UnmarshalInt16LSBMSB(data []byte) (int16, error) {
|
||||
if len(data) < 4 {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
lsb := int16(binary.LittleEndian.Uint16(data[0:2]))
|
||||
msb := int16(binary.BigEndian.Uint16(data[2:4]))
|
||||
|
||||
if lsb != msb {
|
||||
return 0, fmt.Errorf("little-endian and big-endian value mismatch: %d != %d", lsb, msb)
|
||||
}
|
||||
|
||||
return lsb, nil
|
||||
}
|
||||
|
||||
// WriteInt32LSBMSB writes a 32-bit integer in both byte orders, as defined in ECMA-119 7.3.3
|
||||
func WriteInt32LSBMSB(dst []byte, value int32) {
|
||||
_ = dst[7] // early bounds check to guarantee safety of writes below
|
||||
binary.LittleEndian.PutUint32(dst[0:4], uint32(value))
|
||||
binary.BigEndian.PutUint32(dst[4:8], uint32(value))
|
||||
}
|
||||
|
||||
// WriteInt16LSBMSB writes a 16-bit integer in both byte orders, as defined in ECMA-119 7.2.3
|
||||
func WriteInt16LSBMSB(dst []byte, value int16) {
|
||||
_ = dst[3] // early bounds check to guarantee safety of writes below
|
||||
binary.LittleEndian.PutUint16(dst[0:2], uint16(value))
|
||||
binary.BigEndian.PutUint16(dst[2:4], uint16(value))
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package iso9660
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
)
|
||||
|
||||
/* The following types of Rock Ridge records are being handled in some way:
|
||||
* - [X] PX (RR 4.1.1: POSIX file attributes)
|
||||
* - [ ] PN (RR 4.1.2: POSIX device number)
|
||||
* - [ ] SL (RR 4.1.3: symbolic link)
|
||||
* - [x] NM (RR 4.1.4: alternate name)
|
||||
* - [ ] CL (RR 4.1.5.1: child link)
|
||||
* - [ ] PL (RR 4.1.5.2: parent link)
|
||||
* - [ ] RE (RR 4.1.5.3: relocated directory)
|
||||
* - [ ] TF (RR 4.1.6: time stamp(s) for a file)
|
||||
* - [ ] SF (RR 4.1.7: file data in sparse file format)
|
||||
*/
|
||||
|
||||
const (
|
||||
RockRidgeIdentifier = "RRIP_1991A"
|
||||
RockRidgeVersion = 1
|
||||
)
|
||||
|
||||
type RockRidgeNameEntry struct {
|
||||
Flags byte
|
||||
Name string
|
||||
}
|
||||
|
||||
func suspHasRockRidge(se SystemUseEntrySlice) (bool, error) {
|
||||
extensions, err := se.GetExtensionRecords()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, entry := range extensions {
|
||||
if entry.Identifier == RockRidgeIdentifier && entry.Version == RockRidgeVersion {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (s SystemUseEntrySlice) GetRockRidgeName() string {
|
||||
var name string
|
||||
|
||||
for _, entry := range s {
|
||||
// There is a continuation flag in the record, but we determine continuation
|
||||
// by simply reading all NM entries.
|
||||
if entry.Type() == "NM" {
|
||||
nm := umarshalRockRidgeNameEntry(entry)
|
||||
name += nm.Name
|
||||
}
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
func (s SystemUseEntrySlice) GetPosixAttr() (fs.FileMode, error) {
|
||||
for _, entry := range s {
|
||||
if entry.Type() == "PX" {
|
||||
// BUG(kdomanski): If there are multiple RR PX entries (which is forbidden by the spec), the reader will use the first one.
|
||||
return umarshalRockRidgeAttrEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("mandatory entry PX not found")
|
||||
}
|
||||
|
||||
func umarshalRockRidgeAttrEntry(e SystemUseEntry) (fs.FileMode, error) {
|
||||
rrMode, err := UnmarshalUint32LSBMSB(e.Data()[0:8])
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("unmarshall RR PX entry: %w", err)
|
||||
}
|
||||
|
||||
S_IFLNK := (rrMode & 0170000) == 0120000
|
||||
S_IFDIR := (rrMode & 0170000) == 0040000
|
||||
|
||||
mode := rrMode & uint32(fs.ModePerm) // UNIX permissions
|
||||
|
||||
if S_IFLNK {
|
||||
mode |= uint32(os.ModeSymlink)
|
||||
}
|
||||
|
||||
if S_IFDIR {
|
||||
mode |= uint32(os.ModeDir)
|
||||
}
|
||||
|
||||
return fs.FileMode(mode), nil
|
||||
}
|
||||
|
||||
func umarshalRockRidgeNameEntry(e SystemUseEntry) *RockRidgeNameEntry {
|
||||
return &RockRidgeNameEntry{
|
||||
Flags: e.Data()[0],
|
||||
Name: string(e.Data()[1:]),
|
||||
}
|
||||
}
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
package iso9660
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
/* The following types of core SUSP records are being handled in some way:
|
||||
* - [x] CE (SUSP 5.1: continuation)
|
||||
* - [ ] PD (SUSP 5.2: padding)
|
||||
* - [x] SP (SUSP 5.3: offset)
|
||||
* - [ ] ST (SUSP 5.4)
|
||||
* - [x] ER (SUSP 5.5: extension record)
|
||||
* - [ ] ES (SUSP 5.6)
|
||||
*/
|
||||
|
||||
// SUSP-112 4.1
|
||||
type SystemUseEntry []byte
|
||||
|
||||
func (e SystemUseEntry) Length() int {
|
||||
return int(e[2])
|
||||
}
|
||||
|
||||
func (e SystemUseEntry) Data() []byte {
|
||||
return e[4:]
|
||||
}
|
||||
|
||||
func (e SystemUseEntry) Type() string {
|
||||
return string(e[:2])
|
||||
}
|
||||
|
||||
type ExtensionRecord struct {
|
||||
Version int
|
||||
Identifier string
|
||||
Descriptor string
|
||||
Source string
|
||||
}
|
||||
|
||||
// See SUSP-112 5.5
|
||||
func ExtensionRecordDecode(e SystemUseEntry) (*ExtensionRecord, error) {
|
||||
if e.Type() != "ER" {
|
||||
return nil, fmt.Errorf("wrong type of record, expected ER")
|
||||
}
|
||||
if e.Length() < 8 {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
identifierLen := int(e[4])
|
||||
if e.Length() < 8+identifierLen {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
descriptorLen := int(e[5])
|
||||
if e.Length() < 8+identifierLen+descriptorLen {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
sourceLen := int(e[6])
|
||||
if e.Length() < 8+identifierLen+descriptorLen+sourceLen {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
return &ExtensionRecord{
|
||||
Version: int(e[7]),
|
||||
Identifier: string(e[8 : 8+identifierLen]),
|
||||
Descriptor: string(e[8+identifierLen : 8+identifierLen+descriptorLen]),
|
||||
Source: string(e[8+identifierLen+descriptorLen : 8+identifierLen+descriptorLen+sourceLen]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// See SUSP-112 5.3
|
||||
func SPRecordDecode(e SystemUseEntry) (*SPRecord, error) {
|
||||
if e.Type() != "SP" {
|
||||
return nil, fmt.Errorf("wrong type of record, expected SP")
|
||||
}
|
||||
if e.Length() < 7 {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
if beByte := e[4]; beByte != 0xBE {
|
||||
return nil, fmt.Errorf("invalid control byte, %x != 0xBE", beByte)
|
||||
}
|
||||
if efByte := e[5]; efByte != 0xEF {
|
||||
return nil, fmt.Errorf("invalid control byte, %x != 0xEF", efByte)
|
||||
}
|
||||
|
||||
return &SPRecord{
|
||||
BytesSkipped: e[6],
|
||||
}, nil
|
||||
}
|
||||
|
||||
type SPRecord struct {
|
||||
BytesSkipped uint8
|
||||
}
|
||||
|
||||
type SystemUseEntrySlice []SystemUseEntry
|
||||
|
||||
func (s SystemUseEntrySlice) GetExtensionRecords() ([]*ExtensionRecord, error) {
|
||||
results := make([]*ExtensionRecord, 0)
|
||||
for _, entry := range s {
|
||||
if entry.Type() == "ER" {
|
||||
er, err := ExtensionRecordDecode(entry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = append(results, er)
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// SUSP-112 5.1
|
||||
type ContinuationEntry struct {
|
||||
blockLocation uint32
|
||||
offset uint32
|
||||
lengthOfArea uint32
|
||||
}
|
||||
|
||||
func umarshalContinuationEntry(e SystemUseEntry) (*ContinuationEntry, error) {
|
||||
if e.Length() != 28 {
|
||||
return nil, fmt.Errorf("invalid ContinuationArea record with length %d instead of 28", e.Length())
|
||||
}
|
||||
|
||||
location, err := UnmarshalUint32LSBMSB(e.Data()[0:8])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("block location: %w", err)
|
||||
}
|
||||
offset, err := UnmarshalUint32LSBMSB(e.Data()[8:16])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("offset: %w", err)
|
||||
}
|
||||
length, err := UnmarshalUint32LSBMSB(e.Data()[16:24])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("length: %w", err)
|
||||
}
|
||||
|
||||
return &ContinuationEntry{
|
||||
blockLocation: location,
|
||||
offset: offset,
|
||||
lengthOfArea: length,
|
||||
}, nil
|
||||
}
|
||||
|
||||
const (
|
||||
SUEType_ContinuationArea = "CE"
|
||||
SUEType_PaddingField = "PD"
|
||||
SUEType_SharingProtocolIndicator = "SP"
|
||||
SUEType_SharingProtocolTerminator = "ST"
|
||||
SUEType_ExtensionsReference = "ER"
|
||||
SUEType_ExtensionSelector = "ES"
|
||||
)
|
||||
|
||||
func splitSystemUseEntries(data []byte, ra io.ReaderAt) ([]SystemUseEntry, error) {
|
||||
output := make([]SystemUseEntry, 0)
|
||||
|
||||
for len(data) > 0 {
|
||||
if len(data) < 4 {
|
||||
// SUSP-112 4
|
||||
// If the remaining allocated space /.../ is less than four bytes long /.../ shall be ignored.
|
||||
break
|
||||
}
|
||||
|
||||
entryLen := int(data[2])
|
||||
if len(data) < entryLen {
|
||||
return nil, fmt.Errorf("splitting System Use entries: %w, expected %d bytes but have only %d", io.ErrUnexpectedEOF, entryLen, len(data))
|
||||
}
|
||||
|
||||
entry := SystemUseEntry(data[:entryLen])
|
||||
|
||||
if entry.Type() == SUEType_ContinuationArea {
|
||||
ce, err := umarshalContinuationEntry(entry)
|
||||
if err != nil {
|
||||
return output, fmt.Errorf("unmarshaling ContinuationEntry: %w", err)
|
||||
}
|
||||
continuation := make([]byte, ce.lengthOfArea)
|
||||
finalOffset := (ce.blockLocation * sectorSize) + ce.offset
|
||||
if _, err := ra.ReadAt(continuation, int64(finalOffset)); err != nil {
|
||||
return output, fmt.Errorf("reading Continuation Area: %w", err)
|
||||
}
|
||||
|
||||
continuedEntries, err := splitSystemUseEntries(continuation, ra)
|
||||
if err != nil {
|
||||
return output, fmt.Errorf("splitting Continuation Area: %w", err)
|
||||
}
|
||||
output = append(output, continuedEntries...)
|
||||
} else {
|
||||
output = append(output, entry)
|
||||
}
|
||||
|
||||
data = data[entryLen:]
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
||||
|
||||
type SUSPMetadata struct {
|
||||
Offset uint8
|
||||
HasRockRidge bool
|
||||
}
|
||||
|
||||
func (sm *SUSPMetadata) Clone() *SUSPMetadata {
|
||||
if sm == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &SUSPMetadata{
|
||||
Offset: sm.Offset,
|
||||
HasRockRidge: sm.HasRockRidge,
|
||||
}
|
||||
}
|
||||
Generated
+14
@@ -0,0 +1,14 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||
.glide/
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2017, Vladimir Jigulin
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
## Udf filesystem golang library
|
||||
- Non-optimized
|
||||
- Some functioal is broken
|
||||
- `recovery()` style error handling interface
|
||||
- Work only with certain iso's
|
||||
|
||||
It's all because I has reached requried functional for me.
|
||||
|
||||
## Example
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"github.com/mogaika/udf"
|
||||
)
|
||||
|
||||
func main() {
|
||||
r, _ := os.Open("example.iso")
|
||||
u := udf.NewUdfFromReader(r)
|
||||
for _, f := range u.ReadDir(nil) {
|
||||
fmt.Printf("%s %-10d %-20s %v\n", f.Mode().String(), f.Size(), f.Name(), f.ModTime())
|
||||
}
|
||||
}
|
||||
```
|
||||
Output:
|
||||
```
|
||||
-r-xr-xr-x 57 system.cnf 2006-02-11 00:00:00 +0000 UTC
|
||||
-r-xr-xr-x 1911580 SCUS_973.99 2006-03-15 00:00:00 +0000 UTC
|
||||
-r-xr-xr-x 278305 ioprp300.img 2005-11-14 00:00:00 +0000 UTC
|
||||
-r-xr-xr-x 6641 sio2man.irx 2005-10-18 00:00:00 +0000 UTC
|
||||
-r-xr-xr-x 15653 dbcman.irx 2005-10-18 00:00:00 +0000 UTC
|
||||
```
|
||||
|
||||
|
||||
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package udf
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/encoding/charmap"
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
func r_u8(b []byte) uint8 {
|
||||
return b[0]
|
||||
}
|
||||
|
||||
func r_i8(b []byte) int8 {
|
||||
return int8(r_u8(b))
|
||||
}
|
||||
|
||||
var rl_u64 = binary.LittleEndian.Uint64
|
||||
|
||||
func rl_u48(b []byte) uint64 {
|
||||
var buf [8]byte
|
||||
copy(buf[:], b[:6])
|
||||
return rl_u64(buf[:])
|
||||
}
|
||||
|
||||
var rl_u32 = binary.LittleEndian.Uint32
|
||||
var rl_u16 = binary.LittleEndian.Uint16
|
||||
|
||||
func rl_i64(b []byte) int64 {
|
||||
return int64(rl_u64(b))
|
||||
}
|
||||
|
||||
func rl_i32(b []byte) int32 {
|
||||
return int32(rl_u32(b))
|
||||
}
|
||||
|
||||
func rl_i16(b []byte) int16 {
|
||||
return int16(rl_u16(b))
|
||||
}
|
||||
|
||||
var rb_u64 = binary.BigEndian.Uint64
|
||||
var rb_u32 = binary.BigEndian.Uint32
|
||||
var rb_u16 = binary.BigEndian.Uint16
|
||||
|
||||
func rb_u8(b []byte) uint8 {
|
||||
return b[0]
|
||||
}
|
||||
|
||||
func rb_i64(b []byte) int64 {
|
||||
return int64(rb_u64(b))
|
||||
}
|
||||
|
||||
func rb_i32(b []byte) int32 {
|
||||
return int32(rb_u32(b))
|
||||
}
|
||||
|
||||
func rb_i16(b []byte) int16 {
|
||||
return int16(rb_u16(b))
|
||||
}
|
||||
|
||||
func r_dstring(b []byte, fieldlen int) string {
|
||||
if fieldlen == 0 {
|
||||
return ""
|
||||
}
|
||||
return string(b[:b[fieldlen-1]])
|
||||
}
|
||||
|
||||
func r_dcharacters(b []byte) string {
|
||||
if len(b) == 0 {
|
||||
return ""
|
||||
}
|
||||
switch b[0] {
|
||||
case 8:
|
||||
s, _, err := transform.Bytes(charmap.Windows1252.NewDecoder(), b[1:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(s)
|
||||
case 16:
|
||||
s, _, err := transform.Bytes(unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM).NewDecoder(), b[1:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(s)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func r_timestamp(b []byte) time.Time {
|
||||
var t time.Time
|
||||
t = t.AddDate(int(rl_u16(b[2:])), int(b[4]), int(b[5]))
|
||||
t.Add(time.Duration(b[6])*time.Hour +
|
||||
time.Duration(b[7])*time.Minute +
|
||||
time.Duration(b[8])*time.Second)
|
||||
return t
|
||||
}
|
||||
+368
@@ -0,0 +1,368 @@
|
||||
package udf
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
DESCRIPTOR_PRIMARY_VOLUME = 0x1
|
||||
DESCRIPTOR_ANCHOR_VOLUME_POINTER = 0x2
|
||||
DESCRIPTOR_VOLUME_POINTER = 0x3
|
||||
DESCRIPTOR_IMPLEMENTATION_USE_VOLUME = 0x4
|
||||
DESCRIPTOR_PARTITION = 0x5
|
||||
DESCRIPTOR_LOGICAL_VOLUME = 0x6
|
||||
DESCRIPTOR_UNALLOCATED = 0x7
|
||||
DESCRIPTOR_TERMINATING = 0x8
|
||||
DESCRIPTOR_FILE_SET = 0x100
|
||||
DESCRIPTOR_IDENTIFIER = 0x101
|
||||
DESCRIPTOR_ALLOCATION_EXTENT = 0x102
|
||||
DESCRIPTOR_INDIRECT_ENTRY = 0x103
|
||||
DESCRIPTOR_TERMINAL_ENTRY = 0x104
|
||||
DESCRIPTOR_FILE_ENTRY = 0x105
|
||||
)
|
||||
|
||||
type Descriptor struct {
|
||||
TagIdentifier uint16
|
||||
DescriptorVersion uint16
|
||||
TagChecksum uint8
|
||||
TagSerialNumber uint16
|
||||
DescriptorCRC uint16
|
||||
DescriptorCRCLength uint16
|
||||
TagLocation uint32
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (d *Descriptor) Data() []byte {
|
||||
buf := make([]byte, len(d.data))
|
||||
copy(buf, d.data[16:])
|
||||
return buf
|
||||
}
|
||||
|
||||
func (d *Descriptor) FromBytes(b []byte) *Descriptor {
|
||||
d.TagIdentifier = rl_u16(b[0:])
|
||||
d.DescriptorVersion = rl_u16(b[2:])
|
||||
d.TagChecksum = r_u8(b[3:])
|
||||
d.TagSerialNumber = rl_u16(b[6:])
|
||||
d.DescriptorCRC = rl_u16(b[8:])
|
||||
d.DescriptorCRCLength = rl_u16(b[10:])
|
||||
d.TagLocation = rl_u32(b[12:])
|
||||
d.data = b[:]
|
||||
return d
|
||||
}
|
||||
|
||||
func NewDescriptor(b []byte) *Descriptor {
|
||||
return new(Descriptor).FromBytes(b)
|
||||
}
|
||||
|
||||
type AnchorVolumeDescriptorPointer struct {
|
||||
Descriptor Descriptor
|
||||
MainVolumeDescriptorSeq Extent
|
||||
ReserveVolumeDescriptorSeq Extent
|
||||
}
|
||||
|
||||
func (ad *AnchorVolumeDescriptorPointer) FromBytes(b []byte) *AnchorVolumeDescriptorPointer {
|
||||
ad.Descriptor.FromBytes(b)
|
||||
ad.MainVolumeDescriptorSeq = NewExtent(b[16:])
|
||||
ad.ReserveVolumeDescriptorSeq = NewExtent(b[24:])
|
||||
return ad
|
||||
}
|
||||
|
||||
func NewAnchorVolumeDescriptorPointer(b []byte) *AnchorVolumeDescriptorPointer {
|
||||
return new(AnchorVolumeDescriptorPointer).FromBytes(b)
|
||||
}
|
||||
|
||||
func (d *Descriptor) AnchorVolumeDescriptorPointer() *AnchorVolumeDescriptorPointer {
|
||||
return NewAnchorVolumeDescriptorPointer(d.data)
|
||||
}
|
||||
|
||||
type PrimaryVolumeDescriptor struct {
|
||||
Descriptor Descriptor
|
||||
VolumeDescriptorSequenceNumber uint32
|
||||
PrimaryVolumeDescriptorNumber uint32
|
||||
VolumeIdentifier string
|
||||
VolumeSequenceNumber uint16
|
||||
MaximumVolumeSequenceNumber uint16
|
||||
InterchangeLevel uint16
|
||||
MaximumInterchangeLevel uint16
|
||||
CharacterSetList uint32
|
||||
MaximumCharacterSetList uint32
|
||||
VolumeSetIdentifier string
|
||||
VolumeAbstract Extent
|
||||
VolumeCopyrightNoticeExtent Extent
|
||||
ApplicationIdentifier EntityID
|
||||
RecordingDateTime time.Time
|
||||
ImplementationIdentifier EntityID
|
||||
ImplementationUse []byte
|
||||
PredecessorVolumeDescriptorSequenceLocation uint32
|
||||
Flags uint16
|
||||
}
|
||||
|
||||
func (pvd *PrimaryVolumeDescriptor) FromBytes(b []byte) *PrimaryVolumeDescriptor {
|
||||
pvd.Descriptor.FromBytes(b)
|
||||
pvd.VolumeDescriptorSequenceNumber = rl_u32(b[16:])
|
||||
pvd.PrimaryVolumeDescriptorNumber = rl_u32(b[20:])
|
||||
pvd.VolumeIdentifier = r_dstring(b[24:], 32)
|
||||
pvd.VolumeSequenceNumber = rl_u16(b[56:])
|
||||
pvd.MaximumVolumeSequenceNumber = rl_u16(b[58:])
|
||||
pvd.InterchangeLevel = rl_u16(b[60:])
|
||||
pvd.MaximumInterchangeLevel = rl_u16(b[62:])
|
||||
pvd.CharacterSetList = rl_u32(b[64:])
|
||||
pvd.MaximumCharacterSetList = rl_u32(b[68:])
|
||||
pvd.VolumeSetIdentifier = r_dstring(b[72:], 128)
|
||||
pvd.VolumeAbstract = NewExtent(b[328:])
|
||||
pvd.VolumeCopyrightNoticeExtent = NewExtent(b[336:])
|
||||
pvd.ApplicationIdentifier = NewEntityID(b[344:])
|
||||
pvd.RecordingDateTime = r_timestamp(b[376:])
|
||||
pvd.ImplementationIdentifier = NewEntityID(b[388:])
|
||||
pvd.ImplementationUse = b[420:484]
|
||||
pvd.PredecessorVolumeDescriptorSequenceLocation = rl_u32(b[484:])
|
||||
pvd.Flags = rl_u16(b[488:])
|
||||
return pvd
|
||||
}
|
||||
|
||||
func NewPrimaryVolumeDescriptor(b []byte) *PrimaryVolumeDescriptor {
|
||||
return new(PrimaryVolumeDescriptor).FromBytes(b)
|
||||
}
|
||||
|
||||
func (d *Descriptor) PrimaryVolumeDescriptor() *PrimaryVolumeDescriptor {
|
||||
return NewPrimaryVolumeDescriptor(d.data)
|
||||
}
|
||||
|
||||
type PartitionDescriptor struct {
|
||||
Descriptor Descriptor
|
||||
VolumeDescriptorSequenceNumber uint32
|
||||
PartitionFlags uint16
|
||||
PartitionNumber uint16
|
||||
PartitionContents EntityID
|
||||
PartitionContentsUse []byte
|
||||
AccessType uint32
|
||||
PartitionStartingLocation uint32
|
||||
PartitionLength uint32
|
||||
ImplementationIdentifier EntityID
|
||||
ImplementationUse []byte
|
||||
}
|
||||
|
||||
func (pd *PartitionDescriptor) FromBytes(b []byte) *PartitionDescriptor {
|
||||
pd.Descriptor.FromBytes(b)
|
||||
pd.VolumeDescriptorSequenceNumber = rl_u32(b[16:])
|
||||
pd.PartitionFlags = rl_u16(b[20:])
|
||||
pd.PartitionNumber = rl_u16(b[22:])
|
||||
pd.PartitionContents = NewEntityID(b[24:])
|
||||
pd.PartitionContentsUse = b[56:184]
|
||||
pd.AccessType = rl_u32(b[184:])
|
||||
pd.PartitionStartingLocation = rl_u32(b[188:])
|
||||
pd.PartitionLength = rl_u32(b[192:])
|
||||
pd.ImplementationIdentifier = NewEntityID(b[196:])
|
||||
pd.ImplementationUse = b[228:356]
|
||||
return pd
|
||||
}
|
||||
|
||||
func NewPartitionDescriptor(b []byte) *PartitionDescriptor {
|
||||
return new(PartitionDescriptor).FromBytes(b)
|
||||
}
|
||||
|
||||
func (d *Descriptor) PartitionDescriptor() *PartitionDescriptor {
|
||||
return NewPartitionDescriptor(d.data)
|
||||
}
|
||||
|
||||
type PartitionMap struct {
|
||||
PartitionMapType uint8
|
||||
PartitionMapLength uint8
|
||||
VolumeSequenceNumber uint16
|
||||
PartitionNumber uint16
|
||||
}
|
||||
|
||||
func (pm *PartitionMap) FromBytes(b []byte) *PartitionMap {
|
||||
pm.PartitionMapType = rb_u8(b[0:])
|
||||
pm.PartitionMapLength = rb_u8(b[1:])
|
||||
pm.VolumeSequenceNumber = rb_u16(b[2:])
|
||||
pm.PartitionNumber = rb_u16(b[4:])
|
||||
return pm
|
||||
}
|
||||
|
||||
type LogicalVolumeDescriptor struct {
|
||||
Descriptor Descriptor
|
||||
VolumeDescriptorSequenceNumber uint32
|
||||
LogicalVolumeIdentifier string
|
||||
LogicalBlockSize uint32
|
||||
DomainIdentifier EntityID
|
||||
LogicalVolumeContentsUse ExtentLong
|
||||
MapTableLength uint32
|
||||
NumberOfPartitionMaps uint32
|
||||
ImplementationIdentifier EntityID
|
||||
ImplementationUse []byte
|
||||
IntegritySequenceExtent Extent
|
||||
PartitionMaps []PartitionMap
|
||||
}
|
||||
|
||||
func (lvd *LogicalVolumeDescriptor) FromBytes(b []byte) *LogicalVolumeDescriptor {
|
||||
lvd.Descriptor.FromBytes(b)
|
||||
lvd.VolumeDescriptorSequenceNumber = rl_u32(b[16:])
|
||||
lvd.LogicalVolumeIdentifier = r_dstring(b[84:], 128)
|
||||
lvd.LogicalBlockSize = rl_u32(b[212:])
|
||||
lvd.DomainIdentifier = NewEntityID(b[216:])
|
||||
lvd.LogicalVolumeContentsUse = NewExtentLong(b[248:])
|
||||
lvd.MapTableLength = rl_u32(b[264:])
|
||||
lvd.NumberOfPartitionMaps = rl_u32(b[268:])
|
||||
lvd.ImplementationIdentifier = NewEntityID(b[272:])
|
||||
lvd.ImplementationUse = b[304:432]
|
||||
lvd.IntegritySequenceExtent = NewExtent(b[432:])
|
||||
lvd.PartitionMaps = make([]PartitionMap, lvd.NumberOfPartitionMaps)
|
||||
for i := range lvd.PartitionMaps {
|
||||
lvd.PartitionMaps[i].FromBytes(b[440+i*6:])
|
||||
}
|
||||
return lvd
|
||||
}
|
||||
|
||||
func NewLogicalVolumeDescriptor(b []byte) *LogicalVolumeDescriptor {
|
||||
return new(LogicalVolumeDescriptor).FromBytes(b)
|
||||
}
|
||||
|
||||
func (d *Descriptor) LogicalVolumeDescriptor() *LogicalVolumeDescriptor {
|
||||
return NewLogicalVolumeDescriptor(d.data)
|
||||
}
|
||||
|
||||
type FileSetDescriptor struct {
|
||||
Descriptor Descriptor
|
||||
RecordingDateTime time.Time
|
||||
InterchangeLevel uint16
|
||||
MaximumInterchangeLevel uint16
|
||||
CharacterSetList uint32
|
||||
MaximumCharacterSetList uint32
|
||||
FileSetNumber uint32
|
||||
FileSetDescriptorNumber uint32
|
||||
LogicalVolumeIdentifier string
|
||||
FileSetIdentifier string
|
||||
CopyrightFileIdentifier string
|
||||
AbstractFileIdentifier string
|
||||
RootDirectoryICB ExtentLong
|
||||
DomainIdentifier EntityID
|
||||
NexExtent ExtentLong
|
||||
}
|
||||
|
||||
func (fsd *FileSetDescriptor) FromBytes(b []byte) *FileSetDescriptor {
|
||||
fsd.Descriptor.FromBytes(b)
|
||||
fsd.RecordingDateTime = r_timestamp(b[16:])
|
||||
fsd.InterchangeLevel = rl_u16(b[28:])
|
||||
fsd.MaximumInterchangeLevel = rl_u16(b[30:])
|
||||
fsd.CharacterSetList = rl_u32(b[32:])
|
||||
fsd.MaximumCharacterSetList = rl_u32(b[36:])
|
||||
fsd.FileSetNumber = rl_u32(b[40:])
|
||||
fsd.FileSetDescriptorNumber = rl_u32(b[44:])
|
||||
fsd.LogicalVolumeIdentifier = r_dstring(b[112:], 128)
|
||||
fsd.FileSetIdentifier = r_dstring(b[304:], 32)
|
||||
fsd.CopyrightFileIdentifier = r_dstring(b[336:], 32)
|
||||
fsd.AbstractFileIdentifier = r_dstring(b[368:], 32)
|
||||
fsd.RootDirectoryICB = NewExtentLong(b[400:])
|
||||
fsd.DomainIdentifier = NewEntityID(b[416:])
|
||||
fsd.NexExtent = NewExtentLong(b[448:])
|
||||
return fsd
|
||||
}
|
||||
|
||||
func NewFileSetDescriptor(b []byte) *FileSetDescriptor {
|
||||
return new(FileSetDescriptor).FromBytes(b)
|
||||
}
|
||||
|
||||
func (d *Descriptor) FileSetDescriptor() *FileSetDescriptor {
|
||||
return NewFileSetDescriptor(d.data)
|
||||
}
|
||||
|
||||
type FileIdentifierDescriptor struct {
|
||||
Descriptor Descriptor
|
||||
FileVersionNumber uint16
|
||||
FileCharacteristics uint8
|
||||
LengthOfFileIdentifier uint8
|
||||
ICB ExtentLong
|
||||
LengthOfImplementationUse uint16
|
||||
ImplementationUse EntityID
|
||||
FileIdentifier string
|
||||
}
|
||||
|
||||
func (fid *FileIdentifierDescriptor) Len() uint64 {
|
||||
l := 38 + uint64(fid.LengthOfImplementationUse) + uint64(fid.LengthOfFileIdentifier)
|
||||
return 4 * ((l + 3) / 4) // padding = 4
|
||||
}
|
||||
|
||||
func (fid *FileIdentifierDescriptor) FromBytes(b []byte) *FileIdentifierDescriptor {
|
||||
fid.Descriptor.FromBytes(b)
|
||||
fid.FileVersionNumber = rl_u16(b[16:])
|
||||
fid.FileCharacteristics = r_u8(b[18:])
|
||||
fid.LengthOfFileIdentifier = r_u8(b[19:])
|
||||
fid.ICB = NewExtentLong(b[20:])
|
||||
fid.LengthOfImplementationUse = rl_u16(b[36:])
|
||||
fid.ImplementationUse = NewEntityID(b[38:])
|
||||
identStart := 38 + fid.LengthOfImplementationUse
|
||||
fid.FileIdentifier = r_dcharacters(b[identStart : fid.LengthOfFileIdentifier+uint8(identStart)])
|
||||
return fid
|
||||
}
|
||||
|
||||
func NewFileIdentifierDescriptor(b []byte) *FileIdentifierDescriptor {
|
||||
return new(FileIdentifierDescriptor).FromBytes(b)
|
||||
}
|
||||
|
||||
func (d *Descriptor) FileIdentifierDescriptor() *FileIdentifierDescriptor {
|
||||
return NewFileIdentifierDescriptor(d.data)
|
||||
}
|
||||
|
||||
type FileEntry struct {
|
||||
Descriptor Descriptor
|
||||
ICBTag *ICBTag
|
||||
Uid uint32
|
||||
Gid uint32
|
||||
Permissions uint32
|
||||
FileLinkCount uint16
|
||||
RecordFormat uint8
|
||||
RecordDisplayAttributes uint8
|
||||
RecordLength uint32
|
||||
InformationLength uint64
|
||||
LogicalBlocksRecorded uint64
|
||||
AccessTime time.Time
|
||||
ModificationTime time.Time
|
||||
AttributeTime time.Time
|
||||
Checkpoint uint32
|
||||
ExtendedAttributeICB ExtentLong
|
||||
ImplementationIdentifier EntityID
|
||||
UniqueId uint64
|
||||
LengthOfExtendedAttributes uint32
|
||||
LengthOfAllocationDescriptors uint32
|
||||
ExtendedAttributes []byte
|
||||
AllocationDescriptors []Extent
|
||||
}
|
||||
|
||||
func (fe *FileEntry) FromBytes(b []byte) *FileEntry {
|
||||
fe.Descriptor.FromBytes(b)
|
||||
fe.ICBTag = NewICBTag(b[16:])
|
||||
fe.Uid = rl_u32(b[36:])
|
||||
fe.Gid = rl_u32(b[40:])
|
||||
fe.Permissions = rl_u32(b[44:])
|
||||
fe.FileLinkCount = rl_u16(b[48:])
|
||||
fe.RecordFormat = r_u8(b[50:])
|
||||
fe.RecordDisplayAttributes = r_u8(b[51:])
|
||||
fe.RecordLength = rl_u32(b[52:])
|
||||
fe.InformationLength = rl_u64(b[56:])
|
||||
fe.LogicalBlocksRecorded = rl_u64(b[64:])
|
||||
fe.AccessTime = r_timestamp(b[72:])
|
||||
fe.ModificationTime = r_timestamp(b[84:])
|
||||
fe.AttributeTime = r_timestamp(b[96:])
|
||||
fe.Checkpoint = rl_u32(b[108:])
|
||||
fe.ExtendedAttributeICB = NewExtentLong(b[112:])
|
||||
fe.ImplementationIdentifier = NewEntityID(b[128:])
|
||||
fe.UniqueId = rl_u64(b[160:])
|
||||
fe.LengthOfExtendedAttributes = rl_u32(b[168:])
|
||||
fe.LengthOfAllocationDescriptors = rl_u32(b[172:])
|
||||
allocDescStart := 176 + fe.LengthOfExtendedAttributes
|
||||
fe.ExtendedAttributes = b[176:allocDescStart]
|
||||
fe.AllocationDescriptors = make([]Extent, fe.LengthOfAllocationDescriptors/8)
|
||||
for i := range fe.AllocationDescriptors {
|
||||
fe.AllocationDescriptors[i] = NewExtent(b[allocDescStart+uint32(i)*8:])
|
||||
}
|
||||
return fe
|
||||
}
|
||||
|
||||
func NewFileEntry(b []byte) *FileEntry {
|
||||
return new(FileEntry).FromBytes(b)
|
||||
}
|
||||
|
||||
func (d *Descriptor) FileEntry() *FileEntry {
|
||||
return NewFileEntry(d.data)
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package udf
|
||||
|
||||
type EntityID struct {
|
||||
Flags uint8
|
||||
Identifier [23]byte
|
||||
IdentifierSuffix [8]byte
|
||||
}
|
||||
|
||||
func NewEntityID(b []byte) EntityID {
|
||||
e := EntityID{Flags: b[0]}
|
||||
copy(e.Identifier[:], b[1:24])
|
||||
copy(e.IdentifierSuffix[:], b[24:32])
|
||||
return e
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package udf
|
||||
|
||||
type Extent struct {
|
||||
Length uint32
|
||||
Location uint32
|
||||
}
|
||||
|
||||
func NewExtent(b []byte) Extent {
|
||||
return Extent{
|
||||
Length: rl_u32(b[0:]),
|
||||
Location: rl_u32(b[4:]),
|
||||
}
|
||||
}
|
||||
|
||||
type ExtentSmall struct {
|
||||
Length uint16
|
||||
Location uint64
|
||||
}
|
||||
|
||||
func NewExtentSmall(b []byte) ExtentSmall {
|
||||
return ExtentSmall{
|
||||
Length: rl_u16(b[0:]),
|
||||
Location: rl_u48(b[2:]),
|
||||
}
|
||||
}
|
||||
|
||||
type ExtentLong struct {
|
||||
Length uint32
|
||||
Location uint64
|
||||
}
|
||||
|
||||
func NewExtentLong(b []byte) ExtentLong {
|
||||
return ExtentLong{
|
||||
Length: rl_u32(b[0:]),
|
||||
Location: rl_u48(b[4:]),
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package udf
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type File struct {
|
||||
Udf *Udf
|
||||
Fid *FileIdentifierDescriptor
|
||||
fe *FileEntry
|
||||
fileEntryPosition uint64
|
||||
}
|
||||
|
||||
func (f *File) GetFileEntryPosition() int64 {
|
||||
return int64(f.fileEntryPosition)
|
||||
}
|
||||
|
||||
func (f *File) GetFileOffset() int64 {
|
||||
return SECTOR_SIZE * (int64(f.FileEntry().AllocationDescriptors[0].Location) + int64(f.Udf.PartitionStart()))
|
||||
}
|
||||
|
||||
func (f *File) FileEntry() *FileEntry {
|
||||
if f.fe == nil {
|
||||
f.fileEntryPosition = f.Fid.ICB.Location
|
||||
f.fe = NewFileEntry(f.Udf.ReadSector(f.Udf.PartitionStart() + f.fileEntryPosition))
|
||||
}
|
||||
return f.fe
|
||||
}
|
||||
|
||||
func (f *File) NewReader() *io.SectionReader {
|
||||
return io.NewSectionReader(f.Udf.r, f.GetFileOffset(), f.Size())
|
||||
}
|
||||
|
||||
func (f *File) Name() string {
|
||||
return f.Fid.FileIdentifier
|
||||
}
|
||||
|
||||
func (f *File) Mode() os.FileMode {
|
||||
var mode os.FileMode
|
||||
|
||||
perms := os.FileMode(f.FileEntry().Permissions)
|
||||
mode |= ((perms >> 0) & 7) << 0
|
||||
mode |= ((perms >> 5) & 7) << 3
|
||||
mode |= ((perms >> 10) & 7) << 6
|
||||
|
||||
if f.IsDir() {
|
||||
mode |= os.ModeDir
|
||||
}
|
||||
|
||||
return mode
|
||||
}
|
||||
|
||||
func (f *File) Size() int64 {
|
||||
return int64(f.FileEntry().InformationLength)
|
||||
}
|
||||
|
||||
func (f *File) ModTime() time.Time {
|
||||
return f.FileEntry().ModificationTime
|
||||
}
|
||||
|
||||
func (f *File) IsDir() bool {
|
||||
// TODO :Fix! This field always 0 :(
|
||||
return f.FileEntry().ICBTag.FileType == 4
|
||||
}
|
||||
|
||||
func (f *File) Sys() interface{} {
|
||||
return f.Fid
|
||||
}
|
||||
|
||||
func (f *File) ReadDir() []File {
|
||||
return f.Udf.ReadDir(f.FileEntry())
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package udf
|
||||
|
||||
type ICBTag struct {
|
||||
PriorRecordedNumberOfDirectEntries uint32
|
||||
StrategyType uint16
|
||||
StrategyParameter uint16
|
||||
MaximumNumberOfEntries uint16
|
||||
FileType uint8
|
||||
ParentICBLocation uint64
|
||||
Flags uint16
|
||||
}
|
||||
|
||||
func (itag *ICBTag) FromBytes(b []byte) *ICBTag {
|
||||
itag.PriorRecordedNumberOfDirectEntries = rl_u32(b[0:])
|
||||
itag.StrategyType = rl_u16(b[4:])
|
||||
itag.StrategyParameter = rl_u16(b[4:])
|
||||
itag.MaximumNumberOfEntries = rl_u16(b[8:])
|
||||
itag.FileType = r_u8(b[1:])
|
||||
itag.ParentICBLocation = rl_u48(b[12:])
|
||||
itag.Flags = rl_u16(b[18:])
|
||||
return itag
|
||||
}
|
||||
|
||||
func NewICBTag(b []byte) *ICBTag {
|
||||
return new(ICBTag).FromBytes(b)
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
package udf
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
const SECTOR_SIZE = 2048
|
||||
|
||||
type Udf struct {
|
||||
r io.ReaderAt
|
||||
isInited bool
|
||||
pvd *PrimaryVolumeDescriptor
|
||||
pd *PartitionDescriptor
|
||||
lvd *LogicalVolumeDescriptor
|
||||
fsd *FileSetDescriptor
|
||||
root_fe *FileEntry
|
||||
}
|
||||
|
||||
func (udf *Udf) PartitionStart() uint64 {
|
||||
if udf.pd == nil {
|
||||
panic(udf)
|
||||
} else {
|
||||
return uint64(udf.pd.PartitionStartingLocation)
|
||||
}
|
||||
}
|
||||
|
||||
func (udf *Udf) GetReader() io.ReaderAt {
|
||||
return udf.r
|
||||
}
|
||||
|
||||
func (udf *Udf) ReadSectors(sectorNumber uint64, sectorsCount uint64) []byte {
|
||||
buf := make([]byte, SECTOR_SIZE*sectorsCount)
|
||||
readed, err := udf.r.ReadAt(buf[:], int64(SECTOR_SIZE*sectorNumber))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if readed != int(SECTOR_SIZE*sectorsCount) {
|
||||
panic(readed)
|
||||
}
|
||||
return buf[:]
|
||||
}
|
||||
|
||||
func (udf *Udf) ReadSector(sectorNumber uint64) []byte {
|
||||
return udf.ReadSectors(sectorNumber, 1)
|
||||
}
|
||||
|
||||
func (udf *Udf) init() {
|
||||
if udf.isInited {
|
||||
return
|
||||
}
|
||||
|
||||
anchorDesc := NewAnchorVolumeDescriptorPointer(udf.ReadSector(256))
|
||||
if anchorDesc.Descriptor.TagIdentifier != DESCRIPTOR_ANCHOR_VOLUME_POINTER {
|
||||
panic(anchorDesc.Descriptor.TagIdentifier)
|
||||
}
|
||||
|
||||
for sector := uint64(anchorDesc.MainVolumeDescriptorSeq.Location); ; sector++ {
|
||||
desc := NewDescriptor(udf.ReadSector(sector))
|
||||
if desc.TagIdentifier == DESCRIPTOR_TERMINATING {
|
||||
break
|
||||
}
|
||||
switch desc.TagIdentifier {
|
||||
case DESCRIPTOR_PRIMARY_VOLUME:
|
||||
udf.pvd = desc.PrimaryVolumeDescriptor()
|
||||
case DESCRIPTOR_PARTITION:
|
||||
udf.pd = desc.PartitionDescriptor()
|
||||
case DESCRIPTOR_LOGICAL_VOLUME:
|
||||
udf.lvd = desc.LogicalVolumeDescriptor()
|
||||
}
|
||||
}
|
||||
|
||||
partitionStart := udf.PartitionStart()
|
||||
|
||||
udf.fsd = NewFileSetDescriptor(udf.ReadSector(partitionStart + udf.lvd.LogicalVolumeContentsUse.Location))
|
||||
udf.root_fe = NewFileEntry(udf.ReadSector(partitionStart + udf.fsd.RootDirectoryICB.Location))
|
||||
|
||||
udf.isInited = true
|
||||
}
|
||||
|
||||
func (udf *Udf) ReadDir(fe *FileEntry) []File {
|
||||
udf.init()
|
||||
|
||||
if fe == nil {
|
||||
fe = udf.root_fe
|
||||
}
|
||||
|
||||
ps := udf.PartitionStart()
|
||||
|
||||
adPos := fe.AllocationDescriptors[0]
|
||||
fdLen := uint64(adPos.Length)
|
||||
|
||||
fdBuf := udf.ReadSectors(ps+uint64(adPos.Location), (fdLen+SECTOR_SIZE-1)/SECTOR_SIZE)
|
||||
fdOff := uint64(0)
|
||||
|
||||
result := make([]File, 0)
|
||||
|
||||
for uint32(fdOff) < adPos.Length {
|
||||
fid := NewFileIdentifierDescriptor(fdBuf[fdOff:])
|
||||
if fid.FileIdentifier != "" {
|
||||
result = append(result, File{
|
||||
Udf: udf,
|
||||
Fid: fid,
|
||||
})
|
||||
}
|
||||
fdOff += fid.Len()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func NewUdfFromReader(r io.ReaderAt) *Udf {
|
||||
udf := &Udf{
|
||||
r: r,
|
||||
isInited: false,
|
||||
}
|
||||
|
||||
return udf
|
||||
}
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
@@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer.
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
* Neither the name of Google LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
|
||||
+2761
-213
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -4,7 +4,7 @@
|
||||
|
||||
// Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing
|
||||
// algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf
|
||||
package bcrypt // import "golang.org/x/crypto/bcrypt"
|
||||
package bcrypt
|
||||
|
||||
// The code is a port of Provos and Mazières's C implementation.
|
||||
import (
|
||||
@@ -50,7 +50,7 @@ func (ih InvalidHashPrefixError) Error() string {
|
||||
type InvalidCostError int
|
||||
|
||||
func (ic InvalidCostError) Error() string {
|
||||
return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), MinCost, MaxCost)
|
||||
return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed inclusive range %d..%d", int(ic), MinCost, MaxCost)
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
+4492
-677
File diff suppressed because it is too large
Load Diff
+1427
-264
File diff suppressed because it is too large
Load Diff
+8
@@ -12,6 +12,8 @@ import (
|
||||
|
||||
// XOF defines the interface to hash functions that
|
||||
// support arbitrary-length output.
|
||||
//
|
||||
// New callers should prefer the standard library [hash.XOF].
|
||||
type XOF interface {
|
||||
// Write absorbs more data into the hash's state. It panics if called
|
||||
// after Read.
|
||||
@@ -47,6 +49,8 @@ const maxOutputLength = (1 << 32) * 64
|
||||
//
|
||||
// A non-nil key turns the hash into a MAC. The key must between
|
||||
// zero and 32 bytes long.
|
||||
//
|
||||
// The result can be safely interface-upgraded to [hash.XOF].
|
||||
func NewXOF(size uint32, key []byte) (XOF, error) {
|
||||
if len(key) > Size {
|
||||
return nil, errKeySize
|
||||
@@ -93,6 +97,10 @@ func (x *xof) Clone() XOF {
|
||||
return &clone
|
||||
}
|
||||
|
||||
func (x *xof) BlockSize() int {
|
||||
return x.d.BlockSize()
|
||||
}
|
||||
|
||||
func (x *xof) Reset() {
|
||||
x.cfg[0] = byte(Size)
|
||||
binary.LittleEndian.PutUint32(x.cfg[4:], uint32(Size)) // leaf length
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// Copyright 2025 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.25
|
||||
|
||||
package blake2b
|
||||
|
||||
import "hash"
|
||||
|
||||
var _ hash.XOF = (*xof)(nil)
|
||||
+9
-1
@@ -16,9 +16,10 @@
|
||||
//
|
||||
// BLAKE2X is a construction to compute hash values larger than 32 bytes. It
|
||||
// can produce hash values between 0 and 65535 bytes.
|
||||
package blake2s // import "golang.org/x/crypto/blake2s"
|
||||
package blake2s
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"hash"
|
||||
@@ -55,6 +56,13 @@ func Sum256(data []byte) [Size]byte {
|
||||
// and BinaryUnmarshaler for state (de)serialization as documented by hash.Hash.
|
||||
func New256(key []byte) (hash.Hash, error) { return newDigest(Size, key) }
|
||||
|
||||
func init() {
|
||||
crypto.RegisterHash(crypto.BLAKE2s_256, func() hash.Hash {
|
||||
h, _ := New256(nil)
|
||||
return h
|
||||
})
|
||||
}
|
||||
|
||||
// New128 returns a new hash.Hash computing the BLAKE2s-128 checksum given a
|
||||
// non-empty key. Note that a 128-bit digest is too small to be secure as a
|
||||
// cryptographic hash and should only be used as a MAC, thus the key argument
|
||||
|
||||
+2160
-419
File diff suppressed because it is too large
Load Diff
-21
@@ -1,21 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.9
|
||||
|
||||
package blake2s
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"hash"
|
||||
)
|
||||
|
||||
func init() {
|
||||
newHash256 := func() hash.Hash {
|
||||
h, _ := New256(nil)
|
||||
return h
|
||||
}
|
||||
|
||||
crypto.RegisterHash(crypto.BLAKE2s_256, newHash256)
|
||||
}
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
// Deprecated: any new system should use AES (from crypto/aes, if necessary in
|
||||
// an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from
|
||||
// golang.org/x/crypto/chacha20poly1305).
|
||||
package blowfish // import "golang.org/x/crypto/blowfish"
|
||||
package blowfish
|
||||
|
||||
// The code is a port of Bruce Schneier's C implementation.
|
||||
// See https://www.schneier.com/blowfish.html.
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (!arm64 && !s390x && !ppc64le) || !gc || purego
|
||||
//go:build (!arm64 && !s390x && !ppc64 && !ppc64le) || !gc || purego
|
||||
|
||||
package chacha20
|
||||
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
//go:build gc && !purego && (ppc64 || ppc64le)
|
||||
|
||||
package chacha20
|
||||
|
||||
Generated
Vendored
+134
-82
@@ -19,7 +19,7 @@
|
||||
// The differences in this and the original implementation are
|
||||
// due to the calling conventions and initialization of constants.
|
||||
|
||||
//go:build gc && !purego
|
||||
//go:build gc && !purego && (ppc64 || ppc64le)
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
@@ -33,27 +33,70 @@
|
||||
#define CONSTBASE R16
|
||||
#define BLOCKS R17
|
||||
|
||||
DATA consts<>+0x00(SB)/8, $0x3320646e61707865
|
||||
DATA consts<>+0x08(SB)/8, $0x6b20657479622d32
|
||||
DATA consts<>+0x10(SB)/8, $0x0000000000000001
|
||||
DATA consts<>+0x18(SB)/8, $0x0000000000000000
|
||||
DATA consts<>+0x20(SB)/8, $0x0000000000000004
|
||||
DATA consts<>+0x28(SB)/8, $0x0000000000000000
|
||||
DATA consts<>+0x30(SB)/8, $0x0a0b08090e0f0c0d
|
||||
DATA consts<>+0x38(SB)/8, $0x0203000106070405
|
||||
DATA consts<>+0x40(SB)/8, $0x090a0b080d0e0f0c
|
||||
DATA consts<>+0x48(SB)/8, $0x0102030005060704
|
||||
DATA consts<>+0x50(SB)/8, $0x6170786561707865
|
||||
DATA consts<>+0x58(SB)/8, $0x6170786561707865
|
||||
DATA consts<>+0x60(SB)/8, $0x3320646e3320646e
|
||||
DATA consts<>+0x68(SB)/8, $0x3320646e3320646e
|
||||
DATA consts<>+0x70(SB)/8, $0x79622d3279622d32
|
||||
DATA consts<>+0x78(SB)/8, $0x79622d3279622d32
|
||||
DATA consts<>+0x80(SB)/8, $0x6b2065746b206574
|
||||
DATA consts<>+0x88(SB)/8, $0x6b2065746b206574
|
||||
DATA consts<>+0x90(SB)/8, $0x0000000100000000
|
||||
DATA consts<>+0x98(SB)/8, $0x0000000300000002
|
||||
GLOBL consts<>(SB), RODATA, $0xa0
|
||||
// for VPERMXOR
|
||||
#define MASK R18
|
||||
|
||||
DATA consts<>+0x00(SB)/4, $0x61707865
|
||||
DATA consts<>+0x04(SB)/4, $0x3320646e
|
||||
DATA consts<>+0x08(SB)/4, $0x79622d32
|
||||
DATA consts<>+0x0c(SB)/4, $0x6b206574
|
||||
DATA consts<>+0x10(SB)/4, $0x00000001
|
||||
DATA consts<>+0x14(SB)/4, $0x00000000
|
||||
DATA consts<>+0x18(SB)/4, $0x00000000
|
||||
DATA consts<>+0x1c(SB)/4, $0x00000000
|
||||
DATA consts<>+0x20(SB)/4, $0x00000004
|
||||
DATA consts<>+0x24(SB)/4, $0x00000000
|
||||
DATA consts<>+0x28(SB)/4, $0x00000000
|
||||
DATA consts<>+0x2c(SB)/4, $0x00000000
|
||||
DATA consts<>+0x30(SB)/4, $0x0e0f0c0d
|
||||
DATA consts<>+0x34(SB)/4, $0x0a0b0809
|
||||
DATA consts<>+0x38(SB)/4, $0x06070405
|
||||
DATA consts<>+0x3c(SB)/4, $0x02030001
|
||||
DATA consts<>+0x40(SB)/4, $0x0d0e0f0c
|
||||
DATA consts<>+0x44(SB)/4, $0x090a0b08
|
||||
DATA consts<>+0x48(SB)/4, $0x05060704
|
||||
DATA consts<>+0x4c(SB)/4, $0x01020300
|
||||
DATA consts<>+0x50(SB)/4, $0x61707865
|
||||
DATA consts<>+0x54(SB)/4, $0x61707865
|
||||
DATA consts<>+0x58(SB)/4, $0x61707865
|
||||
DATA consts<>+0x5c(SB)/4, $0x61707865
|
||||
DATA consts<>+0x60(SB)/4, $0x3320646e
|
||||
DATA consts<>+0x64(SB)/4, $0x3320646e
|
||||
DATA consts<>+0x68(SB)/4, $0x3320646e
|
||||
DATA consts<>+0x6c(SB)/4, $0x3320646e
|
||||
DATA consts<>+0x70(SB)/4, $0x79622d32
|
||||
DATA consts<>+0x74(SB)/4, $0x79622d32
|
||||
DATA consts<>+0x78(SB)/4, $0x79622d32
|
||||
DATA consts<>+0x7c(SB)/4, $0x79622d32
|
||||
DATA consts<>+0x80(SB)/4, $0x6b206574
|
||||
DATA consts<>+0x84(SB)/4, $0x6b206574
|
||||
DATA consts<>+0x88(SB)/4, $0x6b206574
|
||||
DATA consts<>+0x8c(SB)/4, $0x6b206574
|
||||
DATA consts<>+0x90(SB)/4, $0x00000000
|
||||
DATA consts<>+0x94(SB)/4, $0x00000001
|
||||
DATA consts<>+0x98(SB)/4, $0x00000002
|
||||
DATA consts<>+0x9c(SB)/4, $0x00000003
|
||||
DATA consts<>+0xa0(SB)/4, $0x11223300
|
||||
DATA consts<>+0xa4(SB)/4, $0x55667744
|
||||
DATA consts<>+0xa8(SB)/4, $0x99aabb88
|
||||
DATA consts<>+0xac(SB)/4, $0xddeeffcc
|
||||
DATA consts<>+0xb0(SB)/4, $0x22330011
|
||||
DATA consts<>+0xb4(SB)/4, $0x66774455
|
||||
DATA consts<>+0xb8(SB)/4, $0xaabb8899
|
||||
DATA consts<>+0xbc(SB)/4, $0xeeffccdd
|
||||
GLOBL consts<>(SB), RODATA, $0xc0
|
||||
|
||||
#ifdef GOARCH_ppc64
|
||||
#define BE_XXBRW_INIT() \
|
||||
LVSL (R0)(R0), V24 \
|
||||
VSPLTISB $3, V25 \
|
||||
VXOR V24, V25, V24 \
|
||||
|
||||
#define BE_XXBRW(vr) VPERM vr, vr, V24, vr
|
||||
#else
|
||||
#define BE_XXBRW_INIT()
|
||||
#define BE_XXBRW(vr)
|
||||
#endif
|
||||
|
||||
//func chaCha20_ctr32_vsx(out, inp *byte, len int, key *[8]uint32, counter *uint32)
|
||||
TEXT ·chaCha20_ctr32_vsx(SB),NOSPLIT,$64-40
|
||||
@@ -70,6 +113,9 @@ TEXT ·chaCha20_ctr32_vsx(SB),NOSPLIT,$64-40
|
||||
MOVD $48, R10
|
||||
MOVD $64, R11
|
||||
SRD $6, LEN, BLOCKS
|
||||
// for VPERMXOR
|
||||
MOVD $consts<>+0xa0(SB), MASK
|
||||
MOVD $16, R20
|
||||
// V16
|
||||
LXVW4X (CONSTBASE)(R0), VS48
|
||||
ADD $80,CONSTBASE
|
||||
@@ -84,9 +130,15 @@ TEXT ·chaCha20_ctr32_vsx(SB),NOSPLIT,$64-40
|
||||
// Clear V27
|
||||
VXOR V27, V27, V27
|
||||
|
||||
BE_XXBRW_INIT()
|
||||
|
||||
// V28
|
||||
LXVW4X (CONSTBASE)(R11), VS60
|
||||
|
||||
// Load mask constants for VPERMXOR
|
||||
LXVW4X (MASK)(R0), V20
|
||||
LXVW4X (MASK)(R20), V21
|
||||
|
||||
// splat slot from V19 -> V26
|
||||
VSPLTW $0, V19, V26
|
||||
|
||||
@@ -97,7 +149,7 @@ TEXT ·chaCha20_ctr32_vsx(SB),NOSPLIT,$64-40
|
||||
|
||||
MOVD $10, R14
|
||||
MOVD R14, CTR
|
||||
|
||||
PCALIGN $16
|
||||
loop_outer_vsx:
|
||||
// V0, V1, V2, V3
|
||||
LXVW4X (R0)(CONSTBASE), VS32
|
||||
@@ -128,22 +180,17 @@ loop_outer_vsx:
|
||||
VSPLTISW $12, V28
|
||||
VSPLTISW $8, V29
|
||||
VSPLTISW $7, V30
|
||||
|
||||
PCALIGN $16
|
||||
loop_vsx:
|
||||
VADDUWM V0, V4, V0
|
||||
VADDUWM V1, V5, V1
|
||||
VADDUWM V2, V6, V2
|
||||
VADDUWM V3, V7, V3
|
||||
|
||||
VXOR V12, V0, V12
|
||||
VXOR V13, V1, V13
|
||||
VXOR V14, V2, V14
|
||||
VXOR V15, V3, V15
|
||||
|
||||
VRLW V12, V27, V12
|
||||
VRLW V13, V27, V13
|
||||
VRLW V14, V27, V14
|
||||
VRLW V15, V27, V15
|
||||
VPERMXOR V12, V0, V21, V12
|
||||
VPERMXOR V13, V1, V21, V13
|
||||
VPERMXOR V14, V2, V21, V14
|
||||
VPERMXOR V15, V3, V21, V15
|
||||
|
||||
VADDUWM V8, V12, V8
|
||||
VADDUWM V9, V13, V9
|
||||
@@ -165,15 +212,10 @@ loop_vsx:
|
||||
VADDUWM V2, V6, V2
|
||||
VADDUWM V3, V7, V3
|
||||
|
||||
VXOR V12, V0, V12
|
||||
VXOR V13, V1, V13
|
||||
VXOR V14, V2, V14
|
||||
VXOR V15, V3, V15
|
||||
|
||||
VRLW V12, V29, V12
|
||||
VRLW V13, V29, V13
|
||||
VRLW V14, V29, V14
|
||||
VRLW V15, V29, V15
|
||||
VPERMXOR V12, V0, V20, V12
|
||||
VPERMXOR V13, V1, V20, V13
|
||||
VPERMXOR V14, V2, V20, V14
|
||||
VPERMXOR V15, V3, V20, V15
|
||||
|
||||
VADDUWM V8, V12, V8
|
||||
VADDUWM V9, V13, V9
|
||||
@@ -195,15 +237,10 @@ loop_vsx:
|
||||
VADDUWM V2, V7, V2
|
||||
VADDUWM V3, V4, V3
|
||||
|
||||
VXOR V15, V0, V15
|
||||
VXOR V12, V1, V12
|
||||
VXOR V13, V2, V13
|
||||
VXOR V14, V3, V14
|
||||
|
||||
VRLW V15, V27, V15
|
||||
VRLW V12, V27, V12
|
||||
VRLW V13, V27, V13
|
||||
VRLW V14, V27, V14
|
||||
VPERMXOR V15, V0, V21, V15
|
||||
VPERMXOR V12, V1, V21, V12
|
||||
VPERMXOR V13, V2, V21, V13
|
||||
VPERMXOR V14, V3, V21, V14
|
||||
|
||||
VADDUWM V10, V15, V10
|
||||
VADDUWM V11, V12, V11
|
||||
@@ -225,15 +262,10 @@ loop_vsx:
|
||||
VADDUWM V2, V7, V2
|
||||
VADDUWM V3, V4, V3
|
||||
|
||||
VXOR V15, V0, V15
|
||||
VXOR V12, V1, V12
|
||||
VXOR V13, V2, V13
|
||||
VXOR V14, V3, V14
|
||||
|
||||
VRLW V15, V29, V15
|
||||
VRLW V12, V29, V12
|
||||
VRLW V13, V29, V13
|
||||
VRLW V14, V29, V14
|
||||
VPERMXOR V15, V0, V20, V15
|
||||
VPERMXOR V12, V1, V20, V12
|
||||
VPERMXOR V13, V2, V20, V13
|
||||
VPERMXOR V14, V3, V20, V14
|
||||
|
||||
VADDUWM V10, V15, V10
|
||||
VADDUWM V11, V12, V11
|
||||
@@ -249,48 +281,48 @@ loop_vsx:
|
||||
VRLW V6, V30, V6
|
||||
VRLW V7, V30, V7
|
||||
VRLW V4, V30, V4
|
||||
BC 16, LT, loop_vsx
|
||||
BDNZ loop_vsx
|
||||
|
||||
VADDUWM V12, V26, V12
|
||||
|
||||
WORD $0x13600F8C // VMRGEW V0, V1, V27
|
||||
WORD $0x13821F8C // VMRGEW V2, V3, V28
|
||||
VMRGEW V0, V1, V27
|
||||
VMRGEW V2, V3, V28
|
||||
|
||||
WORD $0x10000E8C // VMRGOW V0, V1, V0
|
||||
WORD $0x10421E8C // VMRGOW V2, V3, V2
|
||||
VMRGOW V0, V1, V0
|
||||
VMRGOW V2, V3, V2
|
||||
|
||||
WORD $0x13A42F8C // VMRGEW V4, V5, V29
|
||||
WORD $0x13C63F8C // VMRGEW V6, V7, V30
|
||||
VMRGEW V4, V5, V29
|
||||
VMRGEW V6, V7, V30
|
||||
|
||||
XXPERMDI VS32, VS34, $0, VS33
|
||||
XXPERMDI VS32, VS34, $3, VS35
|
||||
XXPERMDI VS59, VS60, $0, VS32
|
||||
XXPERMDI VS59, VS60, $3, VS34
|
||||
|
||||
WORD $0x10842E8C // VMRGOW V4, V5, V4
|
||||
WORD $0x10C63E8C // VMRGOW V6, V7, V6
|
||||
VMRGOW V4, V5, V4
|
||||
VMRGOW V6, V7, V6
|
||||
|
||||
WORD $0x13684F8C // VMRGEW V8, V9, V27
|
||||
WORD $0x138A5F8C // VMRGEW V10, V11, V28
|
||||
VMRGEW V8, V9, V27
|
||||
VMRGEW V10, V11, V28
|
||||
|
||||
XXPERMDI VS36, VS38, $0, VS37
|
||||
XXPERMDI VS36, VS38, $3, VS39
|
||||
XXPERMDI VS61, VS62, $0, VS36
|
||||
XXPERMDI VS61, VS62, $3, VS38
|
||||
|
||||
WORD $0x11084E8C // VMRGOW V8, V9, V8
|
||||
WORD $0x114A5E8C // VMRGOW V10, V11, V10
|
||||
VMRGOW V8, V9, V8
|
||||
VMRGOW V10, V11, V10
|
||||
|
||||
WORD $0x13AC6F8C // VMRGEW V12, V13, V29
|
||||
WORD $0x13CE7F8C // VMRGEW V14, V15, V30
|
||||
VMRGEW V12, V13, V29
|
||||
VMRGEW V14, V15, V30
|
||||
|
||||
XXPERMDI VS40, VS42, $0, VS41
|
||||
XXPERMDI VS40, VS42, $3, VS43
|
||||
XXPERMDI VS59, VS60, $0, VS40
|
||||
XXPERMDI VS59, VS60, $3, VS42
|
||||
|
||||
WORD $0x118C6E8C // VMRGOW V12, V13, V12
|
||||
WORD $0x11CE7E8C // VMRGOW V14, V15, V14
|
||||
VMRGOW V12, V13, V12
|
||||
VMRGOW V14, V15, V14
|
||||
|
||||
VSPLTISW $4, V27
|
||||
VADDUWM V26, V27, V26
|
||||
@@ -305,6 +337,11 @@ loop_vsx:
|
||||
VADDUWM V8, V18, V8
|
||||
VADDUWM V12, V19, V12
|
||||
|
||||
BE_XXBRW(V0)
|
||||
BE_XXBRW(V4)
|
||||
BE_XXBRW(V8)
|
||||
BE_XXBRW(V12)
|
||||
|
||||
CMPU LEN, $64
|
||||
BLT tail_vsx
|
||||
|
||||
@@ -333,6 +370,11 @@ loop_vsx:
|
||||
VADDUWM V9, V18, V8
|
||||
VADDUWM V13, V19, V12
|
||||
|
||||
BE_XXBRW(V0)
|
||||
BE_XXBRW(V4)
|
||||
BE_XXBRW(V8)
|
||||
BE_XXBRW(V12)
|
||||
|
||||
CMPU LEN, $64
|
||||
BLT tail_vsx
|
||||
|
||||
@@ -340,8 +382,8 @@ loop_vsx:
|
||||
LXVW4X (INP)(R8), VS60
|
||||
LXVW4X (INP)(R9), VS61
|
||||
LXVW4X (INP)(R10), VS62
|
||||
VXOR V27, V0, V27
|
||||
|
||||
VXOR V27, V0, V27
|
||||
VXOR V28, V4, V28
|
||||
VXOR V29, V8, V29
|
||||
VXOR V30, V12, V30
|
||||
@@ -360,6 +402,11 @@ loop_vsx:
|
||||
VADDUWM V10, V18, V8
|
||||
VADDUWM V14, V19, V12
|
||||
|
||||
BE_XXBRW(V0)
|
||||
BE_XXBRW(V4)
|
||||
BE_XXBRW(V8)
|
||||
BE_XXBRW(V12)
|
||||
|
||||
CMPU LEN, $64
|
||||
BLT tail_vsx
|
||||
|
||||
@@ -387,6 +434,11 @@ loop_vsx:
|
||||
VADDUWM V11, V18, V8
|
||||
VADDUWM V15, V19, V12
|
||||
|
||||
BE_XXBRW(V0)
|
||||
BE_XXBRW(V4)
|
||||
BE_XXBRW(V8)
|
||||
BE_XXBRW(V12)
|
||||
|
||||
CMPU LEN, $64
|
||||
BLT tail_vsx
|
||||
|
||||
@@ -414,9 +466,9 @@ loop_vsx:
|
||||
|
||||
done_vsx:
|
||||
// Increment counter by number of 64 byte blocks
|
||||
MOVD (CNT), R14
|
||||
MOVWZ (CNT), R14
|
||||
ADD BLOCKS, R14
|
||||
MOVD R14, (CNT)
|
||||
MOVWZ R14, (CNT)
|
||||
RET
|
||||
|
||||
tail_vsx:
|
||||
@@ -431,7 +483,7 @@ tail_vsx:
|
||||
ADD $-1, R11, R12
|
||||
ADD $-1, INP
|
||||
ADD $-1, OUT
|
||||
|
||||
PCALIGN $16
|
||||
looptail_vsx:
|
||||
// Copying the result to OUT
|
||||
// in bytes.
|
||||
@@ -439,7 +491,7 @@ looptail_vsx:
|
||||
MOVBZU 1(INP), TMP
|
||||
XOR KEY, TMP, KEY
|
||||
MOVBU KEY, 1(OUT)
|
||||
BC 16, LT, looptail_vsx
|
||||
BDNZ looptail_vsx
|
||||
|
||||
// Clear the stack values
|
||||
STXVW4X VS48, (R11)(R0)
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
// Package chacha20poly1305 implements the ChaCha20-Poly1305 AEAD and its
|
||||
// extended nonce variant XChaCha20-Poly1305, as specified in RFC 8439 and
|
||||
// draft-irtf-cfrg-xchacha-01.
|
||||
package chacha20poly1305 // import "golang.org/x/crypto/chacha20poly1305"
|
||||
package chacha20poly1305
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
|
||||
+9270
-2223
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -234,7 +234,7 @@ func (b *Builder) AddASN1(tag asn1.Tag, f BuilderContinuation) {
|
||||
// Identifiers with the low five bits set indicate high-tag-number format
|
||||
// (two or more octets), which we don't support.
|
||||
if tag&0x1f == 0x1f {
|
||||
b.err = fmt.Errorf("cryptobyte: high-tag number identifier octects not supported: 0x%x", tag)
|
||||
b.err = fmt.Errorf("cryptobyte: high-tag number identifier octets not supported: 0x%x", tag)
|
||||
return
|
||||
}
|
||||
b.AddUint8(uint8(tag))
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
|
||||
// Package asn1 contains supporting types for parsing and building ASN.1
|
||||
// messages with the cryptobyte package.
|
||||
package asn1 // import "golang.org/x/crypto/cryptobyte/asn1"
|
||||
package asn1
|
||||
|
||||
// Tag represents an ASN.1 identifier octet, consisting of a tag number
|
||||
// (indicating a type) and class (such as context-specific or constructed).
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
//
|
||||
// See the documentation and examples for the Builder and String types to get
|
||||
// started.
|
||||
package cryptobyte // import "golang.org/x/crypto/cryptobyte"
|
||||
package cryptobyte
|
||||
|
||||
// String represents a string of bytes. It provides methods for parsing
|
||||
// fixed-length and length-prefixed values from it.
|
||||
|
||||
+35
-4
@@ -6,9 +6,11 @@
|
||||
// performs scalar multiplication on the elliptic curve known as Curve25519.
|
||||
// See RFC 7748.
|
||||
//
|
||||
// Starting in Go 1.20, this package is a wrapper for the X25519 implementation
|
||||
// This package is a wrapper for the X25519 implementation
|
||||
// in the crypto/ecdh package.
|
||||
package curve25519 // import "golang.org/x/crypto/curve25519"
|
||||
package curve25519
|
||||
|
||||
import "crypto/ecdh"
|
||||
|
||||
// ScalarMult sets dst to the product scalar * point.
|
||||
//
|
||||
@@ -16,7 +18,13 @@ package curve25519 // import "golang.org/x/crypto/curve25519"
|
||||
// zeroes, irrespective of the scalar. Instead, use the X25519 function, which
|
||||
// will return an error.
|
||||
func ScalarMult(dst, scalar, point *[32]byte) {
|
||||
scalarMult(dst, scalar, point)
|
||||
if _, err := x25519(dst, scalar[:], point[:]); err != nil {
|
||||
// The only error condition for x25519 when the inputs are 32 bytes long
|
||||
// is if the output would have been the all-zero value.
|
||||
for i := range dst {
|
||||
dst[i] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ScalarBaseMult sets dst to the product scalar * base where base is the
|
||||
@@ -25,7 +33,12 @@ func ScalarMult(dst, scalar, point *[32]byte) {
|
||||
// It is recommended to use the X25519 function with Basepoint instead, as
|
||||
// copying into fixed size arrays can lead to unexpected bugs.
|
||||
func ScalarBaseMult(dst, scalar *[32]byte) {
|
||||
scalarBaseMult(dst, scalar)
|
||||
curve := ecdh.X25519()
|
||||
priv, err := curve.NewPrivateKey(scalar[:])
|
||||
if err != nil {
|
||||
panic("curve25519: internal error: scalarBaseMult was not 32 bytes")
|
||||
}
|
||||
copy(dst[:], priv.PublicKey().Bytes())
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -57,3 +70,21 @@ func X25519(scalar, point []byte) ([]byte, error) {
|
||||
var dst [32]byte
|
||||
return x25519(&dst, scalar, point)
|
||||
}
|
||||
|
||||
func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) {
|
||||
curve := ecdh.X25519()
|
||||
pub, err := curve.NewPublicKey(point)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
priv, err := curve.NewPrivateKey(scalar)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := priv.ECDH(pub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(dst[:], out)
|
||||
return dst[:], nil
|
||||
}
|
||||
|
||||
-105
@@ -1,105 +0,0 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.20
|
||||
|
||||
package curve25519
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/crypto/curve25519/internal/field"
|
||||
)
|
||||
|
||||
func scalarMult(dst, scalar, point *[32]byte) {
|
||||
var e [32]byte
|
||||
|
||||
copy(e[:], scalar[:])
|
||||
e[0] &= 248
|
||||
e[31] &= 127
|
||||
e[31] |= 64
|
||||
|
||||
var x1, x2, z2, x3, z3, tmp0, tmp1 field.Element
|
||||
x1.SetBytes(point[:])
|
||||
x2.One()
|
||||
x3.Set(&x1)
|
||||
z3.One()
|
||||
|
||||
swap := 0
|
||||
for pos := 254; pos >= 0; pos-- {
|
||||
b := e[pos/8] >> uint(pos&7)
|
||||
b &= 1
|
||||
swap ^= int(b)
|
||||
x2.Swap(&x3, swap)
|
||||
z2.Swap(&z3, swap)
|
||||
swap = int(b)
|
||||
|
||||
tmp0.Subtract(&x3, &z3)
|
||||
tmp1.Subtract(&x2, &z2)
|
||||
x2.Add(&x2, &z2)
|
||||
z2.Add(&x3, &z3)
|
||||
z3.Multiply(&tmp0, &x2)
|
||||
z2.Multiply(&z2, &tmp1)
|
||||
tmp0.Square(&tmp1)
|
||||
tmp1.Square(&x2)
|
||||
x3.Add(&z3, &z2)
|
||||
z2.Subtract(&z3, &z2)
|
||||
x2.Multiply(&tmp1, &tmp0)
|
||||
tmp1.Subtract(&tmp1, &tmp0)
|
||||
z2.Square(&z2)
|
||||
|
||||
z3.Mult32(&tmp1, 121666)
|
||||
x3.Square(&x3)
|
||||
tmp0.Add(&tmp0, &z3)
|
||||
z3.Multiply(&x1, &z2)
|
||||
z2.Multiply(&tmp1, &tmp0)
|
||||
}
|
||||
|
||||
x2.Swap(&x3, swap)
|
||||
z2.Swap(&z3, swap)
|
||||
|
||||
z2.Invert(&z2)
|
||||
x2.Multiply(&x2, &z2)
|
||||
copy(dst[:], x2.Bytes())
|
||||
}
|
||||
|
||||
func scalarBaseMult(dst, scalar *[32]byte) {
|
||||
checkBasepoint()
|
||||
scalarMult(dst, scalar, &basePoint)
|
||||
}
|
||||
|
||||
func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) {
|
||||
var in [32]byte
|
||||
if l := len(scalar); l != 32 {
|
||||
return nil, errors.New("bad scalar length: " + strconv.Itoa(l) + ", expected 32")
|
||||
}
|
||||
if l := len(point); l != 32 {
|
||||
return nil, errors.New("bad point length: " + strconv.Itoa(l) + ", expected 32")
|
||||
}
|
||||
copy(in[:], scalar)
|
||||
if &point[0] == &Basepoint[0] {
|
||||
scalarBaseMult(dst, &in)
|
||||
} else {
|
||||
var base, zero [32]byte
|
||||
copy(base[:], point)
|
||||
scalarMult(dst, &in, &base)
|
||||
if subtle.ConstantTimeCompare(dst[:], zero[:]) == 1 {
|
||||
return nil, errors.New("bad input point: low order point")
|
||||
}
|
||||
}
|
||||
return dst[:], nil
|
||||
}
|
||||
|
||||
func checkBasepoint() {
|
||||
if subtle.ConstantTimeCompare(Basepoint, []byte{
|
||||
0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}) != 1 {
|
||||
panic("curve25519: global Basepoint value was modified")
|
||||
}
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.20
|
||||
|
||||
package curve25519
|
||||
|
||||
import "crypto/ecdh"
|
||||
|
||||
func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) {
|
||||
curve := ecdh.X25519()
|
||||
pub, err := curve.NewPublicKey(point)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
priv, err := curve.NewPrivateKey(scalar)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := priv.ECDH(pub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(dst[:], out)
|
||||
return dst[:], nil
|
||||
}
|
||||
|
||||
func scalarMult(dst, scalar, point *[32]byte) {
|
||||
if _, err := x25519(dst, scalar[:], point[:]); err != nil {
|
||||
// The only error condition for x25519 when the inputs are 32 bytes long
|
||||
// is if the output would have been the all-zero value.
|
||||
for i := range dst {
|
||||
dst[i] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func scalarBaseMult(dst, scalar *[32]byte) {
|
||||
curve := ecdh.X25519()
|
||||
priv, err := curve.NewPrivateKey(scalar[:])
|
||||
if err != nil {
|
||||
panic("curve25519: internal error: scalarBaseMult was not 32 bytes")
|
||||
}
|
||||
copy(dst[:], priv.PublicKey().Bytes())
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
This package is kept in sync with crypto/ed25519/internal/edwards25519/field in
|
||||
the standard library.
|
||||
|
||||
If there are any changes in the standard library that need to be synced to this
|
||||
package, run sync.sh. It will not overwrite any local changes made since the
|
||||
previous sync, so it's ok to land changes in this package first, and then sync
|
||||
to the standard library later.
|
||||
-416
@@ -1,416 +0,0 @@
|
||||
// Copyright (c) 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package field implements fast arithmetic modulo 2^255-19.
|
||||
package field
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// Element represents an element of the field GF(2^255-19). Note that this
|
||||
// is not a cryptographically secure group, and should only be used to interact
|
||||
// with edwards25519.Point coordinates.
|
||||
//
|
||||
// This type works similarly to math/big.Int, and all arguments and receivers
|
||||
// are allowed to alias.
|
||||
//
|
||||
// The zero value is a valid zero element.
|
||||
type Element struct {
|
||||
// An element t represents the integer
|
||||
// t.l0 + t.l1*2^51 + t.l2*2^102 + t.l3*2^153 + t.l4*2^204
|
||||
//
|
||||
// Between operations, all limbs are expected to be lower than 2^52.
|
||||
l0 uint64
|
||||
l1 uint64
|
||||
l2 uint64
|
||||
l3 uint64
|
||||
l4 uint64
|
||||
}
|
||||
|
||||
const maskLow51Bits uint64 = (1 << 51) - 1
|
||||
|
||||
var feZero = &Element{0, 0, 0, 0, 0}
|
||||
|
||||
// Zero sets v = 0, and returns v.
|
||||
func (v *Element) Zero() *Element {
|
||||
*v = *feZero
|
||||
return v
|
||||
}
|
||||
|
||||
var feOne = &Element{1, 0, 0, 0, 0}
|
||||
|
||||
// One sets v = 1, and returns v.
|
||||
func (v *Element) One() *Element {
|
||||
*v = *feOne
|
||||
return v
|
||||
}
|
||||
|
||||
// reduce reduces v modulo 2^255 - 19 and returns it.
|
||||
func (v *Element) reduce() *Element {
|
||||
v.carryPropagate()
|
||||
|
||||
// After the light reduction we now have a field element representation
|
||||
// v < 2^255 + 2^13 * 19, but need v < 2^255 - 19.
|
||||
|
||||
// If v >= 2^255 - 19, then v + 19 >= 2^255, which would overflow 2^255 - 1,
|
||||
// generating a carry. That is, c will be 0 if v < 2^255 - 19, and 1 otherwise.
|
||||
c := (v.l0 + 19) >> 51
|
||||
c = (v.l1 + c) >> 51
|
||||
c = (v.l2 + c) >> 51
|
||||
c = (v.l3 + c) >> 51
|
||||
c = (v.l4 + c) >> 51
|
||||
|
||||
// If v < 2^255 - 19 and c = 0, this will be a no-op. Otherwise, it's
|
||||
// effectively applying the reduction identity to the carry.
|
||||
v.l0 += 19 * c
|
||||
|
||||
v.l1 += v.l0 >> 51
|
||||
v.l0 = v.l0 & maskLow51Bits
|
||||
v.l2 += v.l1 >> 51
|
||||
v.l1 = v.l1 & maskLow51Bits
|
||||
v.l3 += v.l2 >> 51
|
||||
v.l2 = v.l2 & maskLow51Bits
|
||||
v.l4 += v.l3 >> 51
|
||||
v.l3 = v.l3 & maskLow51Bits
|
||||
// no additional carry
|
||||
v.l4 = v.l4 & maskLow51Bits
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Add sets v = a + b, and returns v.
|
||||
func (v *Element) Add(a, b *Element) *Element {
|
||||
v.l0 = a.l0 + b.l0
|
||||
v.l1 = a.l1 + b.l1
|
||||
v.l2 = a.l2 + b.l2
|
||||
v.l3 = a.l3 + b.l3
|
||||
v.l4 = a.l4 + b.l4
|
||||
// Using the generic implementation here is actually faster than the
|
||||
// assembly. Probably because the body of this function is so simple that
|
||||
// the compiler can figure out better optimizations by inlining the carry
|
||||
// propagation. TODO
|
||||
return v.carryPropagateGeneric()
|
||||
}
|
||||
|
||||
// Subtract sets v = a - b, and returns v.
|
||||
func (v *Element) Subtract(a, b *Element) *Element {
|
||||
// We first add 2 * p, to guarantee the subtraction won't underflow, and
|
||||
// then subtract b (which can be up to 2^255 + 2^13 * 19).
|
||||
v.l0 = (a.l0 + 0xFFFFFFFFFFFDA) - b.l0
|
||||
v.l1 = (a.l1 + 0xFFFFFFFFFFFFE) - b.l1
|
||||
v.l2 = (a.l2 + 0xFFFFFFFFFFFFE) - b.l2
|
||||
v.l3 = (a.l3 + 0xFFFFFFFFFFFFE) - b.l3
|
||||
v.l4 = (a.l4 + 0xFFFFFFFFFFFFE) - b.l4
|
||||
return v.carryPropagate()
|
||||
}
|
||||
|
||||
// Negate sets v = -a, and returns v.
|
||||
func (v *Element) Negate(a *Element) *Element {
|
||||
return v.Subtract(feZero, a)
|
||||
}
|
||||
|
||||
// Invert sets v = 1/z mod p, and returns v.
|
||||
//
|
||||
// If z == 0, Invert returns v = 0.
|
||||
func (v *Element) Invert(z *Element) *Element {
|
||||
// Inversion is implemented as exponentiation with exponent p − 2. It uses the
|
||||
// same sequence of 255 squarings and 11 multiplications as [Curve25519].
|
||||
var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t Element
|
||||
|
||||
z2.Square(z) // 2
|
||||
t.Square(&z2) // 4
|
||||
t.Square(&t) // 8
|
||||
z9.Multiply(&t, z) // 9
|
||||
z11.Multiply(&z9, &z2) // 11
|
||||
t.Square(&z11) // 22
|
||||
z2_5_0.Multiply(&t, &z9) // 31 = 2^5 - 2^0
|
||||
|
||||
t.Square(&z2_5_0) // 2^6 - 2^1
|
||||
for i := 0; i < 4; i++ {
|
||||
t.Square(&t) // 2^10 - 2^5
|
||||
}
|
||||
z2_10_0.Multiply(&t, &z2_5_0) // 2^10 - 2^0
|
||||
|
||||
t.Square(&z2_10_0) // 2^11 - 2^1
|
||||
for i := 0; i < 9; i++ {
|
||||
t.Square(&t) // 2^20 - 2^10
|
||||
}
|
||||
z2_20_0.Multiply(&t, &z2_10_0) // 2^20 - 2^0
|
||||
|
||||
t.Square(&z2_20_0) // 2^21 - 2^1
|
||||
for i := 0; i < 19; i++ {
|
||||
t.Square(&t) // 2^40 - 2^20
|
||||
}
|
||||
t.Multiply(&t, &z2_20_0) // 2^40 - 2^0
|
||||
|
||||
t.Square(&t) // 2^41 - 2^1
|
||||
for i := 0; i < 9; i++ {
|
||||
t.Square(&t) // 2^50 - 2^10
|
||||
}
|
||||
z2_50_0.Multiply(&t, &z2_10_0) // 2^50 - 2^0
|
||||
|
||||
t.Square(&z2_50_0) // 2^51 - 2^1
|
||||
for i := 0; i < 49; i++ {
|
||||
t.Square(&t) // 2^100 - 2^50
|
||||
}
|
||||
z2_100_0.Multiply(&t, &z2_50_0) // 2^100 - 2^0
|
||||
|
||||
t.Square(&z2_100_0) // 2^101 - 2^1
|
||||
for i := 0; i < 99; i++ {
|
||||
t.Square(&t) // 2^200 - 2^100
|
||||
}
|
||||
t.Multiply(&t, &z2_100_0) // 2^200 - 2^0
|
||||
|
||||
t.Square(&t) // 2^201 - 2^1
|
||||
for i := 0; i < 49; i++ {
|
||||
t.Square(&t) // 2^250 - 2^50
|
||||
}
|
||||
t.Multiply(&t, &z2_50_0) // 2^250 - 2^0
|
||||
|
||||
t.Square(&t) // 2^251 - 2^1
|
||||
t.Square(&t) // 2^252 - 2^2
|
||||
t.Square(&t) // 2^253 - 2^3
|
||||
t.Square(&t) // 2^254 - 2^4
|
||||
t.Square(&t) // 2^255 - 2^5
|
||||
|
||||
return v.Multiply(&t, &z11) // 2^255 - 21
|
||||
}
|
||||
|
||||
// Set sets v = a, and returns v.
|
||||
func (v *Element) Set(a *Element) *Element {
|
||||
*v = *a
|
||||
return v
|
||||
}
|
||||
|
||||
// SetBytes sets v to x, which must be a 32-byte little-endian encoding.
|
||||
//
|
||||
// Consistent with RFC 7748, the most significant bit (the high bit of the
|
||||
// last byte) is ignored, and non-canonical values (2^255-19 through 2^255-1)
|
||||
// are accepted. Note that this is laxer than specified by RFC 8032.
|
||||
func (v *Element) SetBytes(x []byte) *Element {
|
||||
if len(x) != 32 {
|
||||
panic("edwards25519: invalid field element input size")
|
||||
}
|
||||
|
||||
// Bits 0:51 (bytes 0:8, bits 0:64, shift 0, mask 51).
|
||||
v.l0 = binary.LittleEndian.Uint64(x[0:8])
|
||||
v.l0 &= maskLow51Bits
|
||||
// Bits 51:102 (bytes 6:14, bits 48:112, shift 3, mask 51).
|
||||
v.l1 = binary.LittleEndian.Uint64(x[6:14]) >> 3
|
||||
v.l1 &= maskLow51Bits
|
||||
// Bits 102:153 (bytes 12:20, bits 96:160, shift 6, mask 51).
|
||||
v.l2 = binary.LittleEndian.Uint64(x[12:20]) >> 6
|
||||
v.l2 &= maskLow51Bits
|
||||
// Bits 153:204 (bytes 19:27, bits 152:216, shift 1, mask 51).
|
||||
v.l3 = binary.LittleEndian.Uint64(x[19:27]) >> 1
|
||||
v.l3 &= maskLow51Bits
|
||||
// Bits 204:251 (bytes 24:32, bits 192:256, shift 12, mask 51).
|
||||
// Note: not bytes 25:33, shift 4, to avoid overread.
|
||||
v.l4 = binary.LittleEndian.Uint64(x[24:32]) >> 12
|
||||
v.l4 &= maskLow51Bits
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Bytes returns the canonical 32-byte little-endian encoding of v.
|
||||
func (v *Element) Bytes() []byte {
|
||||
// This function is outlined to make the allocations inline in the caller
|
||||
// rather than happen on the heap.
|
||||
var out [32]byte
|
||||
return v.bytes(&out)
|
||||
}
|
||||
|
||||
func (v *Element) bytes(out *[32]byte) []byte {
|
||||
t := *v
|
||||
t.reduce()
|
||||
|
||||
var buf [8]byte
|
||||
for i, l := range [5]uint64{t.l0, t.l1, t.l2, t.l3, t.l4} {
|
||||
bitsOffset := i * 51
|
||||
binary.LittleEndian.PutUint64(buf[:], l<<uint(bitsOffset%8))
|
||||
for i, bb := range buf {
|
||||
off := bitsOffset/8 + i
|
||||
if off >= len(out) {
|
||||
break
|
||||
}
|
||||
out[off] |= bb
|
||||
}
|
||||
}
|
||||
|
||||
return out[:]
|
||||
}
|
||||
|
||||
// Equal returns 1 if v and u are equal, and 0 otherwise.
|
||||
func (v *Element) Equal(u *Element) int {
|
||||
sa, sv := u.Bytes(), v.Bytes()
|
||||
return subtle.ConstantTimeCompare(sa, sv)
|
||||
}
|
||||
|
||||
// mask64Bits returns 0xffffffff if cond is 1, and 0 otherwise.
|
||||
func mask64Bits(cond int) uint64 { return ^(uint64(cond) - 1) }
|
||||
|
||||
// Select sets v to a if cond == 1, and to b if cond == 0.
|
||||
func (v *Element) Select(a, b *Element, cond int) *Element {
|
||||
m := mask64Bits(cond)
|
||||
v.l0 = (m & a.l0) | (^m & b.l0)
|
||||
v.l1 = (m & a.l1) | (^m & b.l1)
|
||||
v.l2 = (m & a.l2) | (^m & b.l2)
|
||||
v.l3 = (m & a.l3) | (^m & b.l3)
|
||||
v.l4 = (m & a.l4) | (^m & b.l4)
|
||||
return v
|
||||
}
|
||||
|
||||
// Swap swaps v and u if cond == 1 or leaves them unchanged if cond == 0, and returns v.
|
||||
func (v *Element) Swap(u *Element, cond int) {
|
||||
m := mask64Bits(cond)
|
||||
t := m & (v.l0 ^ u.l0)
|
||||
v.l0 ^= t
|
||||
u.l0 ^= t
|
||||
t = m & (v.l1 ^ u.l1)
|
||||
v.l1 ^= t
|
||||
u.l1 ^= t
|
||||
t = m & (v.l2 ^ u.l2)
|
||||
v.l2 ^= t
|
||||
u.l2 ^= t
|
||||
t = m & (v.l3 ^ u.l3)
|
||||
v.l3 ^= t
|
||||
u.l3 ^= t
|
||||
t = m & (v.l4 ^ u.l4)
|
||||
v.l4 ^= t
|
||||
u.l4 ^= t
|
||||
}
|
||||
|
||||
// IsNegative returns 1 if v is negative, and 0 otherwise.
|
||||
func (v *Element) IsNegative() int {
|
||||
return int(v.Bytes()[0] & 1)
|
||||
}
|
||||
|
||||
// Absolute sets v to |u|, and returns v.
|
||||
func (v *Element) Absolute(u *Element) *Element {
|
||||
return v.Select(new(Element).Negate(u), u, u.IsNegative())
|
||||
}
|
||||
|
||||
// Multiply sets v = x * y, and returns v.
|
||||
func (v *Element) Multiply(x, y *Element) *Element {
|
||||
feMul(v, x, y)
|
||||
return v
|
||||
}
|
||||
|
||||
// Square sets v = x * x, and returns v.
|
||||
func (v *Element) Square(x *Element) *Element {
|
||||
feSquare(v, x)
|
||||
return v
|
||||
}
|
||||
|
||||
// Mult32 sets v = x * y, and returns v.
|
||||
func (v *Element) Mult32(x *Element, y uint32) *Element {
|
||||
x0lo, x0hi := mul51(x.l0, y)
|
||||
x1lo, x1hi := mul51(x.l1, y)
|
||||
x2lo, x2hi := mul51(x.l2, y)
|
||||
x3lo, x3hi := mul51(x.l3, y)
|
||||
x4lo, x4hi := mul51(x.l4, y)
|
||||
v.l0 = x0lo + 19*x4hi // carried over per the reduction identity
|
||||
v.l1 = x1lo + x0hi
|
||||
v.l2 = x2lo + x1hi
|
||||
v.l3 = x3lo + x2hi
|
||||
v.l4 = x4lo + x3hi
|
||||
// The hi portions are going to be only 32 bits, plus any previous excess,
|
||||
// so we can skip the carry propagation.
|
||||
return v
|
||||
}
|
||||
|
||||
// mul51 returns lo + hi * 2⁵¹ = a * b.
|
||||
func mul51(a uint64, b uint32) (lo uint64, hi uint64) {
|
||||
mh, ml := bits.Mul64(a, uint64(b))
|
||||
lo = ml & maskLow51Bits
|
||||
hi = (mh << 13) | (ml >> 51)
|
||||
return
|
||||
}
|
||||
|
||||
// Pow22523 set v = x^((p-5)/8), and returns v. (p-5)/8 is 2^252-3.
|
||||
func (v *Element) Pow22523(x *Element) *Element {
|
||||
var t0, t1, t2 Element
|
||||
|
||||
t0.Square(x) // x^2
|
||||
t1.Square(&t0) // x^4
|
||||
t1.Square(&t1) // x^8
|
||||
t1.Multiply(x, &t1) // x^9
|
||||
t0.Multiply(&t0, &t1) // x^11
|
||||
t0.Square(&t0) // x^22
|
||||
t0.Multiply(&t1, &t0) // x^31
|
||||
t1.Square(&t0) // x^62
|
||||
for i := 1; i < 5; i++ { // x^992
|
||||
t1.Square(&t1)
|
||||
}
|
||||
t0.Multiply(&t1, &t0) // x^1023 -> 1023 = 2^10 - 1
|
||||
t1.Square(&t0) // 2^11 - 2
|
||||
for i := 1; i < 10; i++ { // 2^20 - 2^10
|
||||
t1.Square(&t1)
|
||||
}
|
||||
t1.Multiply(&t1, &t0) // 2^20 - 1
|
||||
t2.Square(&t1) // 2^21 - 2
|
||||
for i := 1; i < 20; i++ { // 2^40 - 2^20
|
||||
t2.Square(&t2)
|
||||
}
|
||||
t1.Multiply(&t2, &t1) // 2^40 - 1
|
||||
t1.Square(&t1) // 2^41 - 2
|
||||
for i := 1; i < 10; i++ { // 2^50 - 2^10
|
||||
t1.Square(&t1)
|
||||
}
|
||||
t0.Multiply(&t1, &t0) // 2^50 - 1
|
||||
t1.Square(&t0) // 2^51 - 2
|
||||
for i := 1; i < 50; i++ { // 2^100 - 2^50
|
||||
t1.Square(&t1)
|
||||
}
|
||||
t1.Multiply(&t1, &t0) // 2^100 - 1
|
||||
t2.Square(&t1) // 2^101 - 2
|
||||
for i := 1; i < 100; i++ { // 2^200 - 2^100
|
||||
t2.Square(&t2)
|
||||
}
|
||||
t1.Multiply(&t2, &t1) // 2^200 - 1
|
||||
t1.Square(&t1) // 2^201 - 2
|
||||
for i := 1; i < 50; i++ { // 2^250 - 2^50
|
||||
t1.Square(&t1)
|
||||
}
|
||||
t0.Multiply(&t1, &t0) // 2^250 - 1
|
||||
t0.Square(&t0) // 2^251 - 2
|
||||
t0.Square(&t0) // 2^252 - 4
|
||||
return v.Multiply(&t0, x) // 2^252 - 3 -> x^(2^252-3)
|
||||
}
|
||||
|
||||
// sqrtM1 is 2^((p-1)/4), which squared is equal to -1 by Euler's Criterion.
|
||||
var sqrtM1 = &Element{1718705420411056, 234908883556509,
|
||||
2233514472574048, 2117202627021982, 765476049583133}
|
||||
|
||||
// SqrtRatio sets r to the non-negative square root of the ratio of u and v.
|
||||
//
|
||||
// If u/v is square, SqrtRatio returns r and 1. If u/v is not square, SqrtRatio
|
||||
// sets r according to Section 4.3 of draft-irtf-cfrg-ristretto255-decaf448-00,
|
||||
// and returns r and 0.
|
||||
func (r *Element) SqrtRatio(u, v *Element) (rr *Element, wasSquare int) {
|
||||
var a, b Element
|
||||
|
||||
// r = (u * v3) * (u * v7)^((p-5)/8)
|
||||
v2 := a.Square(v)
|
||||
uv3 := b.Multiply(u, b.Multiply(v2, v))
|
||||
uv7 := a.Multiply(uv3, a.Square(v2))
|
||||
r.Multiply(uv3, r.Pow22523(uv7))
|
||||
|
||||
check := a.Multiply(v, a.Square(r)) // check = v * r^2
|
||||
|
||||
uNeg := b.Negate(u)
|
||||
correctSignSqrt := check.Equal(u)
|
||||
flippedSignSqrt := check.Equal(uNeg)
|
||||
flippedSignSqrtI := check.Equal(uNeg.Multiply(uNeg, sqrtM1))
|
||||
|
||||
rPrime := b.Multiply(r, sqrtM1) // r_prime = SQRT_M1 * r
|
||||
// r = CT_SELECT(r_prime IF flipped_sign_sqrt | flipped_sign_sqrt_i ELSE r)
|
||||
r.Select(rPrime, r, flippedSignSqrt|flippedSignSqrtI)
|
||||
|
||||
r.Absolute(r) // Choose the nonnegative square root.
|
||||
return r, correctSignSqrt | flippedSignSqrt
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
|
||||
|
||||
//go:build amd64 && gc && !purego
|
||||
|
||||
package field
|
||||
|
||||
// feMul sets out = a * b. It works like feMulGeneric.
|
||||
//
|
||||
//go:noescape
|
||||
func feMul(out *Element, a *Element, b *Element)
|
||||
|
||||
// feSquare sets out = a * a. It works like feSquareGeneric.
|
||||
//
|
||||
//go:noescape
|
||||
func feSquare(out *Element, a *Element)
|
||||
-378
@@ -1,378 +0,0 @@
|
||||
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
|
||||
|
||||
//go:build amd64 && gc && !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func feMul(out *Element, a *Element, b *Element)
|
||||
TEXT ·feMul(SB), NOSPLIT, $0-24
|
||||
MOVQ a+8(FP), CX
|
||||
MOVQ b+16(FP), BX
|
||||
|
||||
// r0 = a0×b0
|
||||
MOVQ (CX), AX
|
||||
MULQ (BX)
|
||||
MOVQ AX, DI
|
||||
MOVQ DX, SI
|
||||
|
||||
// r0 += 19×a1×b4
|
||||
MOVQ 8(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 32(BX)
|
||||
ADDQ AX, DI
|
||||
ADCQ DX, SI
|
||||
|
||||
// r0 += 19×a2×b3
|
||||
MOVQ 16(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 24(BX)
|
||||
ADDQ AX, DI
|
||||
ADCQ DX, SI
|
||||
|
||||
// r0 += 19×a3×b2
|
||||
MOVQ 24(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 16(BX)
|
||||
ADDQ AX, DI
|
||||
ADCQ DX, SI
|
||||
|
||||
// r0 += 19×a4×b1
|
||||
MOVQ 32(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 8(BX)
|
||||
ADDQ AX, DI
|
||||
ADCQ DX, SI
|
||||
|
||||
// r1 = a0×b1
|
||||
MOVQ (CX), AX
|
||||
MULQ 8(BX)
|
||||
MOVQ AX, R9
|
||||
MOVQ DX, R8
|
||||
|
||||
// r1 += a1×b0
|
||||
MOVQ 8(CX), AX
|
||||
MULQ (BX)
|
||||
ADDQ AX, R9
|
||||
ADCQ DX, R8
|
||||
|
||||
// r1 += 19×a2×b4
|
||||
MOVQ 16(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 32(BX)
|
||||
ADDQ AX, R9
|
||||
ADCQ DX, R8
|
||||
|
||||
// r1 += 19×a3×b3
|
||||
MOVQ 24(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 24(BX)
|
||||
ADDQ AX, R9
|
||||
ADCQ DX, R8
|
||||
|
||||
// r1 += 19×a4×b2
|
||||
MOVQ 32(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 16(BX)
|
||||
ADDQ AX, R9
|
||||
ADCQ DX, R8
|
||||
|
||||
// r2 = a0×b2
|
||||
MOVQ (CX), AX
|
||||
MULQ 16(BX)
|
||||
MOVQ AX, R11
|
||||
MOVQ DX, R10
|
||||
|
||||
// r2 += a1×b1
|
||||
MOVQ 8(CX), AX
|
||||
MULQ 8(BX)
|
||||
ADDQ AX, R11
|
||||
ADCQ DX, R10
|
||||
|
||||
// r2 += a2×b0
|
||||
MOVQ 16(CX), AX
|
||||
MULQ (BX)
|
||||
ADDQ AX, R11
|
||||
ADCQ DX, R10
|
||||
|
||||
// r2 += 19×a3×b4
|
||||
MOVQ 24(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 32(BX)
|
||||
ADDQ AX, R11
|
||||
ADCQ DX, R10
|
||||
|
||||
// r2 += 19×a4×b3
|
||||
MOVQ 32(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 24(BX)
|
||||
ADDQ AX, R11
|
||||
ADCQ DX, R10
|
||||
|
||||
// r3 = a0×b3
|
||||
MOVQ (CX), AX
|
||||
MULQ 24(BX)
|
||||
MOVQ AX, R13
|
||||
MOVQ DX, R12
|
||||
|
||||
// r3 += a1×b2
|
||||
MOVQ 8(CX), AX
|
||||
MULQ 16(BX)
|
||||
ADDQ AX, R13
|
||||
ADCQ DX, R12
|
||||
|
||||
// r3 += a2×b1
|
||||
MOVQ 16(CX), AX
|
||||
MULQ 8(BX)
|
||||
ADDQ AX, R13
|
||||
ADCQ DX, R12
|
||||
|
||||
// r3 += a3×b0
|
||||
MOVQ 24(CX), AX
|
||||
MULQ (BX)
|
||||
ADDQ AX, R13
|
||||
ADCQ DX, R12
|
||||
|
||||
// r3 += 19×a4×b4
|
||||
MOVQ 32(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 32(BX)
|
||||
ADDQ AX, R13
|
||||
ADCQ DX, R12
|
||||
|
||||
// r4 = a0×b4
|
||||
MOVQ (CX), AX
|
||||
MULQ 32(BX)
|
||||
MOVQ AX, R15
|
||||
MOVQ DX, R14
|
||||
|
||||
// r4 += a1×b3
|
||||
MOVQ 8(CX), AX
|
||||
MULQ 24(BX)
|
||||
ADDQ AX, R15
|
||||
ADCQ DX, R14
|
||||
|
||||
// r4 += a2×b2
|
||||
MOVQ 16(CX), AX
|
||||
MULQ 16(BX)
|
||||
ADDQ AX, R15
|
||||
ADCQ DX, R14
|
||||
|
||||
// r4 += a3×b1
|
||||
MOVQ 24(CX), AX
|
||||
MULQ 8(BX)
|
||||
ADDQ AX, R15
|
||||
ADCQ DX, R14
|
||||
|
||||
// r4 += a4×b0
|
||||
MOVQ 32(CX), AX
|
||||
MULQ (BX)
|
||||
ADDQ AX, R15
|
||||
ADCQ DX, R14
|
||||
|
||||
// First reduction chain
|
||||
MOVQ $0x0007ffffffffffff, AX
|
||||
SHLQ $0x0d, DI, SI
|
||||
SHLQ $0x0d, R9, R8
|
||||
SHLQ $0x0d, R11, R10
|
||||
SHLQ $0x0d, R13, R12
|
||||
SHLQ $0x0d, R15, R14
|
||||
ANDQ AX, DI
|
||||
IMUL3Q $0x13, R14, R14
|
||||
ADDQ R14, DI
|
||||
ANDQ AX, R9
|
||||
ADDQ SI, R9
|
||||
ANDQ AX, R11
|
||||
ADDQ R8, R11
|
||||
ANDQ AX, R13
|
||||
ADDQ R10, R13
|
||||
ANDQ AX, R15
|
||||
ADDQ R12, R15
|
||||
|
||||
// Second reduction chain (carryPropagate)
|
||||
MOVQ DI, SI
|
||||
SHRQ $0x33, SI
|
||||
MOVQ R9, R8
|
||||
SHRQ $0x33, R8
|
||||
MOVQ R11, R10
|
||||
SHRQ $0x33, R10
|
||||
MOVQ R13, R12
|
||||
SHRQ $0x33, R12
|
||||
MOVQ R15, R14
|
||||
SHRQ $0x33, R14
|
||||
ANDQ AX, DI
|
||||
IMUL3Q $0x13, R14, R14
|
||||
ADDQ R14, DI
|
||||
ANDQ AX, R9
|
||||
ADDQ SI, R9
|
||||
ANDQ AX, R11
|
||||
ADDQ R8, R11
|
||||
ANDQ AX, R13
|
||||
ADDQ R10, R13
|
||||
ANDQ AX, R15
|
||||
ADDQ R12, R15
|
||||
|
||||
// Store output
|
||||
MOVQ out+0(FP), AX
|
||||
MOVQ DI, (AX)
|
||||
MOVQ R9, 8(AX)
|
||||
MOVQ R11, 16(AX)
|
||||
MOVQ R13, 24(AX)
|
||||
MOVQ R15, 32(AX)
|
||||
RET
|
||||
|
||||
// func feSquare(out *Element, a *Element)
|
||||
TEXT ·feSquare(SB), NOSPLIT, $0-16
|
||||
MOVQ a+8(FP), CX
|
||||
|
||||
// r0 = l0×l0
|
||||
MOVQ (CX), AX
|
||||
MULQ (CX)
|
||||
MOVQ AX, SI
|
||||
MOVQ DX, BX
|
||||
|
||||
// r0 += 38×l1×l4
|
||||
MOVQ 8(CX), AX
|
||||
IMUL3Q $0x26, AX, AX
|
||||
MULQ 32(CX)
|
||||
ADDQ AX, SI
|
||||
ADCQ DX, BX
|
||||
|
||||
// r0 += 38×l2×l3
|
||||
MOVQ 16(CX), AX
|
||||
IMUL3Q $0x26, AX, AX
|
||||
MULQ 24(CX)
|
||||
ADDQ AX, SI
|
||||
ADCQ DX, BX
|
||||
|
||||
// r1 = 2×l0×l1
|
||||
MOVQ (CX), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 8(CX)
|
||||
MOVQ AX, R8
|
||||
MOVQ DX, DI
|
||||
|
||||
// r1 += 38×l2×l4
|
||||
MOVQ 16(CX), AX
|
||||
IMUL3Q $0x26, AX, AX
|
||||
MULQ 32(CX)
|
||||
ADDQ AX, R8
|
||||
ADCQ DX, DI
|
||||
|
||||
// r1 += 19×l3×l3
|
||||
MOVQ 24(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 24(CX)
|
||||
ADDQ AX, R8
|
||||
ADCQ DX, DI
|
||||
|
||||
// r2 = 2×l0×l2
|
||||
MOVQ (CX), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 16(CX)
|
||||
MOVQ AX, R10
|
||||
MOVQ DX, R9
|
||||
|
||||
// r2 += l1×l1
|
||||
MOVQ 8(CX), AX
|
||||
MULQ 8(CX)
|
||||
ADDQ AX, R10
|
||||
ADCQ DX, R9
|
||||
|
||||
// r2 += 38×l3×l4
|
||||
MOVQ 24(CX), AX
|
||||
IMUL3Q $0x26, AX, AX
|
||||
MULQ 32(CX)
|
||||
ADDQ AX, R10
|
||||
ADCQ DX, R9
|
||||
|
||||
// r3 = 2×l0×l3
|
||||
MOVQ (CX), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 24(CX)
|
||||
MOVQ AX, R12
|
||||
MOVQ DX, R11
|
||||
|
||||
// r3 += 2×l1×l2
|
||||
MOVQ 8(CX), AX
|
||||
IMUL3Q $0x02, AX, AX
|
||||
MULQ 16(CX)
|
||||
ADDQ AX, R12
|
||||
ADCQ DX, R11
|
||||
|
||||
// r3 += 19×l4×l4
|
||||
MOVQ 32(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 32(CX)
|
||||
ADDQ AX, R12
|
||||
ADCQ DX, R11
|
||||
|
||||
// r4 = 2×l0×l4
|
||||
MOVQ (CX), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 32(CX)
|
||||
MOVQ AX, R14
|
||||
MOVQ DX, R13
|
||||
|
||||
// r4 += 2×l1×l3
|
||||
MOVQ 8(CX), AX
|
||||
IMUL3Q $0x02, AX, AX
|
||||
MULQ 24(CX)
|
||||
ADDQ AX, R14
|
||||
ADCQ DX, R13
|
||||
|
||||
// r4 += l2×l2
|
||||
MOVQ 16(CX), AX
|
||||
MULQ 16(CX)
|
||||
ADDQ AX, R14
|
||||
ADCQ DX, R13
|
||||
|
||||
// First reduction chain
|
||||
MOVQ $0x0007ffffffffffff, AX
|
||||
SHLQ $0x0d, SI, BX
|
||||
SHLQ $0x0d, R8, DI
|
||||
SHLQ $0x0d, R10, R9
|
||||
SHLQ $0x0d, R12, R11
|
||||
SHLQ $0x0d, R14, R13
|
||||
ANDQ AX, SI
|
||||
IMUL3Q $0x13, R13, R13
|
||||
ADDQ R13, SI
|
||||
ANDQ AX, R8
|
||||
ADDQ BX, R8
|
||||
ANDQ AX, R10
|
||||
ADDQ DI, R10
|
||||
ANDQ AX, R12
|
||||
ADDQ R9, R12
|
||||
ANDQ AX, R14
|
||||
ADDQ R11, R14
|
||||
|
||||
// Second reduction chain (carryPropagate)
|
||||
MOVQ SI, BX
|
||||
SHRQ $0x33, BX
|
||||
MOVQ R8, DI
|
||||
SHRQ $0x33, DI
|
||||
MOVQ R10, R9
|
||||
SHRQ $0x33, R9
|
||||
MOVQ R12, R11
|
||||
SHRQ $0x33, R11
|
||||
MOVQ R14, R13
|
||||
SHRQ $0x33, R13
|
||||
ANDQ AX, SI
|
||||
IMUL3Q $0x13, R13, R13
|
||||
ADDQ R13, SI
|
||||
ANDQ AX, R8
|
||||
ADDQ BX, R8
|
||||
ANDQ AX, R10
|
||||
ADDQ DI, R10
|
||||
ANDQ AX, R12
|
||||
ADDQ R9, R12
|
||||
ANDQ AX, R14
|
||||
ADDQ R11, R14
|
||||
|
||||
// Store output
|
||||
MOVQ out+0(FP), AX
|
||||
MOVQ SI, (AX)
|
||||
MOVQ R8, 8(AX)
|
||||
MOVQ R10, 16(AX)
|
||||
MOVQ R12, 24(AX)
|
||||
MOVQ R14, 32(AX)
|
||||
RET
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
// Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !amd64 || !gc || purego
|
||||
|
||||
package field
|
||||
|
||||
func feMul(v, x, y *Element) { feMulGeneric(v, x, y) }
|
||||
|
||||
func feSquare(v, x *Element) { feSquareGeneric(v, x) }
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
// Copyright (c) 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build arm64 && gc && !purego
|
||||
|
||||
package field
|
||||
|
||||
//go:noescape
|
||||
func carryPropagate(v *Element)
|
||||
|
||||
func (v *Element) carryPropagate() *Element {
|
||||
carryPropagate(v)
|
||||
return v
|
||||
}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
// Copyright (c) 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build arm64 && gc && !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// carryPropagate works exactly like carryPropagateGeneric and uses the
|
||||
// same AND, ADD, and LSR+MADD instructions emitted by the compiler, but
|
||||
// avoids loading R0-R4 twice and uses LDP and STP.
|
||||
//
|
||||
// See https://golang.org/issues/43145 for the main compiler issue.
|
||||
//
|
||||
// func carryPropagate(v *Element)
|
||||
TEXT ·carryPropagate(SB),NOFRAME|NOSPLIT,$0-8
|
||||
MOVD v+0(FP), R20
|
||||
|
||||
LDP 0(R20), (R0, R1)
|
||||
LDP 16(R20), (R2, R3)
|
||||
MOVD 32(R20), R4
|
||||
|
||||
AND $0x7ffffffffffff, R0, R10
|
||||
AND $0x7ffffffffffff, R1, R11
|
||||
AND $0x7ffffffffffff, R2, R12
|
||||
AND $0x7ffffffffffff, R3, R13
|
||||
AND $0x7ffffffffffff, R4, R14
|
||||
|
||||
ADD R0>>51, R11, R11
|
||||
ADD R1>>51, R12, R12
|
||||
ADD R2>>51, R13, R13
|
||||
ADD R3>>51, R14, R14
|
||||
// R4>>51 * 19 + R10 -> R10
|
||||
LSR $51, R4, R21
|
||||
MOVD $19, R22
|
||||
MADD R22, R10, R21, R10
|
||||
|
||||
STP (R10, R11), 0(R20)
|
||||
STP (R12, R13), 16(R20)
|
||||
MOVD R14, 32(R20)
|
||||
|
||||
RET
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
// Copyright (c) 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !arm64 || !gc || purego
|
||||
|
||||
package field
|
||||
|
||||
func (v *Element) carryPropagate() *Element {
|
||||
return v.carryPropagateGeneric()
|
||||
}
|
||||
-264
@@ -1,264 +0,0 @@
|
||||
// Copyright (c) 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package field
|
||||
|
||||
import "math/bits"
|
||||
|
||||
// uint128 holds a 128-bit number as two 64-bit limbs, for use with the
|
||||
// bits.Mul64 and bits.Add64 intrinsics.
|
||||
type uint128 struct {
|
||||
lo, hi uint64
|
||||
}
|
||||
|
||||
// mul64 returns a * b.
|
||||
func mul64(a, b uint64) uint128 {
|
||||
hi, lo := bits.Mul64(a, b)
|
||||
return uint128{lo, hi}
|
||||
}
|
||||
|
||||
// addMul64 returns v + a * b.
|
||||
func addMul64(v uint128, a, b uint64) uint128 {
|
||||
hi, lo := bits.Mul64(a, b)
|
||||
lo, c := bits.Add64(lo, v.lo, 0)
|
||||
hi, _ = bits.Add64(hi, v.hi, c)
|
||||
return uint128{lo, hi}
|
||||
}
|
||||
|
||||
// shiftRightBy51 returns a >> 51. a is assumed to be at most 115 bits.
|
||||
func shiftRightBy51(a uint128) uint64 {
|
||||
return (a.hi << (64 - 51)) | (a.lo >> 51)
|
||||
}
|
||||
|
||||
func feMulGeneric(v, a, b *Element) {
|
||||
a0 := a.l0
|
||||
a1 := a.l1
|
||||
a2 := a.l2
|
||||
a3 := a.l3
|
||||
a4 := a.l4
|
||||
|
||||
b0 := b.l0
|
||||
b1 := b.l1
|
||||
b2 := b.l2
|
||||
b3 := b.l3
|
||||
b4 := b.l4
|
||||
|
||||
// Limb multiplication works like pen-and-paper columnar multiplication, but
|
||||
// with 51-bit limbs instead of digits.
|
||||
//
|
||||
// a4 a3 a2 a1 a0 x
|
||||
// b4 b3 b2 b1 b0 =
|
||||
// ------------------------
|
||||
// a4b0 a3b0 a2b0 a1b0 a0b0 +
|
||||
// a4b1 a3b1 a2b1 a1b1 a0b1 +
|
||||
// a4b2 a3b2 a2b2 a1b2 a0b2 +
|
||||
// a4b3 a3b3 a2b3 a1b3 a0b3 +
|
||||
// a4b4 a3b4 a2b4 a1b4 a0b4 =
|
||||
// ----------------------------------------------
|
||||
// r8 r7 r6 r5 r4 r3 r2 r1 r0
|
||||
//
|
||||
// We can then use the reduction identity (a * 2²⁵⁵ + b = a * 19 + b) to
|
||||
// reduce the limbs that would overflow 255 bits. r5 * 2²⁵⁵ becomes 19 * r5,
|
||||
// r6 * 2³⁰⁶ becomes 19 * r6 * 2⁵¹, etc.
|
||||
//
|
||||
// Reduction can be carried out simultaneously to multiplication. For
|
||||
// example, we do not compute r5: whenever the result of a multiplication
|
||||
// belongs to r5, like a1b4, we multiply it by 19 and add the result to r0.
|
||||
//
|
||||
// a4b0 a3b0 a2b0 a1b0 a0b0 +
|
||||
// a3b1 a2b1 a1b1 a0b1 19×a4b1 +
|
||||
// a2b2 a1b2 a0b2 19×a4b2 19×a3b2 +
|
||||
// a1b3 a0b3 19×a4b3 19×a3b3 19×a2b3 +
|
||||
// a0b4 19×a4b4 19×a3b4 19×a2b4 19×a1b4 =
|
||||
// --------------------------------------
|
||||
// r4 r3 r2 r1 r0
|
||||
//
|
||||
// Finally we add up the columns into wide, overlapping limbs.
|
||||
|
||||
a1_19 := a1 * 19
|
||||
a2_19 := a2 * 19
|
||||
a3_19 := a3 * 19
|
||||
a4_19 := a4 * 19
|
||||
|
||||
// r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1)
|
||||
r0 := mul64(a0, b0)
|
||||
r0 = addMul64(r0, a1_19, b4)
|
||||
r0 = addMul64(r0, a2_19, b3)
|
||||
r0 = addMul64(r0, a3_19, b2)
|
||||
r0 = addMul64(r0, a4_19, b1)
|
||||
|
||||
// r1 = a0×b1 + a1×b0 + 19×(a2×b4 + a3×b3 + a4×b2)
|
||||
r1 := mul64(a0, b1)
|
||||
r1 = addMul64(r1, a1, b0)
|
||||
r1 = addMul64(r1, a2_19, b4)
|
||||
r1 = addMul64(r1, a3_19, b3)
|
||||
r1 = addMul64(r1, a4_19, b2)
|
||||
|
||||
// r2 = a0×b2 + a1×b1 + a2×b0 + 19×(a3×b4 + a4×b3)
|
||||
r2 := mul64(a0, b2)
|
||||
r2 = addMul64(r2, a1, b1)
|
||||
r2 = addMul64(r2, a2, b0)
|
||||
r2 = addMul64(r2, a3_19, b4)
|
||||
r2 = addMul64(r2, a4_19, b3)
|
||||
|
||||
// r3 = a0×b3 + a1×b2 + a2×b1 + a3×b0 + 19×a4×b4
|
||||
r3 := mul64(a0, b3)
|
||||
r3 = addMul64(r3, a1, b2)
|
||||
r3 = addMul64(r3, a2, b1)
|
||||
r3 = addMul64(r3, a3, b0)
|
||||
r3 = addMul64(r3, a4_19, b4)
|
||||
|
||||
// r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0
|
||||
r4 := mul64(a0, b4)
|
||||
r4 = addMul64(r4, a1, b3)
|
||||
r4 = addMul64(r4, a2, b2)
|
||||
r4 = addMul64(r4, a3, b1)
|
||||
r4 = addMul64(r4, a4, b0)
|
||||
|
||||
// After the multiplication, we need to reduce (carry) the five coefficients
|
||||
// to obtain a result with limbs that are at most slightly larger than 2⁵¹,
|
||||
// to respect the Element invariant.
|
||||
//
|
||||
// Overall, the reduction works the same as carryPropagate, except with
|
||||
// wider inputs: we take the carry for each coefficient by shifting it right
|
||||
// by 51, and add it to the limb above it. The top carry is multiplied by 19
|
||||
// according to the reduction identity and added to the lowest limb.
|
||||
//
|
||||
// The largest coefficient (r0) will be at most 111 bits, which guarantees
|
||||
// that all carries are at most 111 - 51 = 60 bits, which fits in a uint64.
|
||||
//
|
||||
// r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1)
|
||||
// r0 < 2⁵²×2⁵² + 19×(2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵²)
|
||||
// r0 < (1 + 19 × 4) × 2⁵² × 2⁵²
|
||||
// r0 < 2⁷ × 2⁵² × 2⁵²
|
||||
// r0 < 2¹¹¹
|
||||
//
|
||||
// Moreover, the top coefficient (r4) is at most 107 bits, so c4 is at most
|
||||
// 56 bits, and c4 * 19 is at most 61 bits, which again fits in a uint64 and
|
||||
// allows us to easily apply the reduction identity.
|
||||
//
|
||||
// r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0
|
||||
// r4 < 5 × 2⁵² × 2⁵²
|
||||
// r4 < 2¹⁰⁷
|
||||
//
|
||||
|
||||
c0 := shiftRightBy51(r0)
|
||||
c1 := shiftRightBy51(r1)
|
||||
c2 := shiftRightBy51(r2)
|
||||
c3 := shiftRightBy51(r3)
|
||||
c4 := shiftRightBy51(r4)
|
||||
|
||||
rr0 := r0.lo&maskLow51Bits + c4*19
|
||||
rr1 := r1.lo&maskLow51Bits + c0
|
||||
rr2 := r2.lo&maskLow51Bits + c1
|
||||
rr3 := r3.lo&maskLow51Bits + c2
|
||||
rr4 := r4.lo&maskLow51Bits + c3
|
||||
|
||||
// Now all coefficients fit into 64-bit registers but are still too large to
|
||||
// be passed around as a Element. We therefore do one last carry chain,
|
||||
// where the carries will be small enough to fit in the wiggle room above 2⁵¹.
|
||||
*v = Element{rr0, rr1, rr2, rr3, rr4}
|
||||
v.carryPropagate()
|
||||
}
|
||||
|
||||
func feSquareGeneric(v, a *Element) {
|
||||
l0 := a.l0
|
||||
l1 := a.l1
|
||||
l2 := a.l2
|
||||
l3 := a.l3
|
||||
l4 := a.l4
|
||||
|
||||
// Squaring works precisely like multiplication above, but thanks to its
|
||||
// symmetry we get to group a few terms together.
|
||||
//
|
||||
// l4 l3 l2 l1 l0 x
|
||||
// l4 l3 l2 l1 l0 =
|
||||
// ------------------------
|
||||
// l4l0 l3l0 l2l0 l1l0 l0l0 +
|
||||
// l4l1 l3l1 l2l1 l1l1 l0l1 +
|
||||
// l4l2 l3l2 l2l2 l1l2 l0l2 +
|
||||
// l4l3 l3l3 l2l3 l1l3 l0l3 +
|
||||
// l4l4 l3l4 l2l4 l1l4 l0l4 =
|
||||
// ----------------------------------------------
|
||||
// r8 r7 r6 r5 r4 r3 r2 r1 r0
|
||||
//
|
||||
// l4l0 l3l0 l2l0 l1l0 l0l0 +
|
||||
// l3l1 l2l1 l1l1 l0l1 19×l4l1 +
|
||||
// l2l2 l1l2 l0l2 19×l4l2 19×l3l2 +
|
||||
// l1l3 l0l3 19×l4l3 19×l3l3 19×l2l3 +
|
||||
// l0l4 19×l4l4 19×l3l4 19×l2l4 19×l1l4 =
|
||||
// --------------------------------------
|
||||
// r4 r3 r2 r1 r0
|
||||
//
|
||||
// With precomputed 2×, 19×, and 2×19× terms, we can compute each limb with
|
||||
// only three Mul64 and four Add64, instead of five and eight.
|
||||
|
||||
l0_2 := l0 * 2
|
||||
l1_2 := l1 * 2
|
||||
|
||||
l1_38 := l1 * 38
|
||||
l2_38 := l2 * 38
|
||||
l3_38 := l3 * 38
|
||||
|
||||
l3_19 := l3 * 19
|
||||
l4_19 := l4 * 19
|
||||
|
||||
// r0 = l0×l0 + 19×(l1×l4 + l2×l3 + l3×l2 + l4×l1) = l0×l0 + 19×2×(l1×l4 + l2×l3)
|
||||
r0 := mul64(l0, l0)
|
||||
r0 = addMul64(r0, l1_38, l4)
|
||||
r0 = addMul64(r0, l2_38, l3)
|
||||
|
||||
// r1 = l0×l1 + l1×l0 + 19×(l2×l4 + l3×l3 + l4×l2) = 2×l0×l1 + 19×2×l2×l4 + 19×l3×l3
|
||||
r1 := mul64(l0_2, l1)
|
||||
r1 = addMul64(r1, l2_38, l4)
|
||||
r1 = addMul64(r1, l3_19, l3)
|
||||
|
||||
// r2 = l0×l2 + l1×l1 + l2×l0 + 19×(l3×l4 + l4×l3) = 2×l0×l2 + l1×l1 + 19×2×l3×l4
|
||||
r2 := mul64(l0_2, l2)
|
||||
r2 = addMul64(r2, l1, l1)
|
||||
r2 = addMul64(r2, l3_38, l4)
|
||||
|
||||
// r3 = l0×l3 + l1×l2 + l2×l1 + l3×l0 + 19×l4×l4 = 2×l0×l3 + 2×l1×l2 + 19×l4×l4
|
||||
r3 := mul64(l0_2, l3)
|
||||
r3 = addMul64(r3, l1_2, l2)
|
||||
r3 = addMul64(r3, l4_19, l4)
|
||||
|
||||
// r4 = l0×l4 + l1×l3 + l2×l2 + l3×l1 + l4×l0 = 2×l0×l4 + 2×l1×l3 + l2×l2
|
||||
r4 := mul64(l0_2, l4)
|
||||
r4 = addMul64(r4, l1_2, l3)
|
||||
r4 = addMul64(r4, l2, l2)
|
||||
|
||||
c0 := shiftRightBy51(r0)
|
||||
c1 := shiftRightBy51(r1)
|
||||
c2 := shiftRightBy51(r2)
|
||||
c3 := shiftRightBy51(r3)
|
||||
c4 := shiftRightBy51(r4)
|
||||
|
||||
rr0 := r0.lo&maskLow51Bits + c4*19
|
||||
rr1 := r1.lo&maskLow51Bits + c0
|
||||
rr2 := r2.lo&maskLow51Bits + c1
|
||||
rr3 := r3.lo&maskLow51Bits + c2
|
||||
rr4 := r4.lo&maskLow51Bits + c3
|
||||
|
||||
*v = Element{rr0, rr1, rr2, rr3, rr4}
|
||||
v.carryPropagate()
|
||||
}
|
||||
|
||||
// carryPropagateGeneric brings the limbs below 52 bits by applying the reduction
|
||||
// identity (a * 2²⁵⁵ + b = a * 19 + b) to the l4 carry. TODO inline
|
||||
func (v *Element) carryPropagateGeneric() *Element {
|
||||
c0 := v.l0 >> 51
|
||||
c1 := v.l1 >> 51
|
||||
c2 := v.l2 >> 51
|
||||
c3 := v.l3 >> 51
|
||||
c4 := v.l4 >> 51
|
||||
|
||||
v.l0 = v.l0&maskLow51Bits + c4*19
|
||||
v.l1 = v.l1&maskLow51Bits + c0
|
||||
v.l2 = v.l2&maskLow51Bits + c1
|
||||
v.l3 = v.l3&maskLow51Bits + c2
|
||||
v.l4 = v.l4&maskLow51Bits + c3
|
||||
|
||||
return v
|
||||
}
|
||||
-1
@@ -1 +0,0 @@
|
||||
b0c49ae9f59d233526f8934262c5bbbe14d4358d
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
#! /bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(git rev-parse --show-toplevel)"
|
||||
|
||||
STD_PATH=src/crypto/ed25519/internal/edwards25519/field
|
||||
LOCAL_PATH=curve25519/internal/field
|
||||
LAST_SYNC_REF=$(cat $LOCAL_PATH/sync.checkpoint)
|
||||
|
||||
git fetch https://go.googlesource.com/go master
|
||||
|
||||
if git diff --quiet $LAST_SYNC_REF:$STD_PATH FETCH_HEAD:$STD_PATH; then
|
||||
echo "No changes."
|
||||
else
|
||||
NEW_REF=$(git rev-parse FETCH_HEAD | tee $LOCAL_PATH/sync.checkpoint)
|
||||
echo "Applying changes from $LAST_SYNC_REF to $NEW_REF..."
|
||||
git diff $LAST_SYNC_REF:$STD_PATH FETCH_HEAD:$STD_PATH | \
|
||||
git apply -3 --directory=$LOCAL_PATH
|
||||
fi
|
||||
+1
-3
@@ -11,9 +11,7 @@
|
||||
// operations with the same key more efficient. This package refers to the RFC
|
||||
// 8032 private key as the “seed”.
|
||||
//
|
||||
// Beginning with Go 1.13, the functionality of this package was moved to the
|
||||
// standard library as crypto/ed25519. This package only acts as a compatibility
|
||||
// wrapper.
|
||||
// This package is a wrapper around the standard library crypto/ed25519 package.
|
||||
package ed25519
|
||||
|
||||
import (
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
// HKDF is a cryptographic key derivation function (KDF) with the goal of
|
||||
// expanding limited input keying material into one or more cryptographically
|
||||
// strong secret keys.
|
||||
package hkdf // import "golang.org/x/crypto/hkdf"
|
||||
package hkdf
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (!amd64 && !ppc64le && !s390x) || !gc || purego
|
||||
//go:build (!amd64 && !loong64 && !ppc64le && !ppc64 && !s390x) || !gc || purego
|
||||
|
||||
package poly1305
|
||||
|
||||
|
||||
+59
-74
@@ -1,108 +1,93 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// Code generated by command: go run sum_amd64_asm.go -out ../sum_amd64.s -pkg poly1305. DO NOT EDIT.
|
||||
|
||||
//go:build gc && !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
#define POLY1305_ADD(msg, h0, h1, h2) \
|
||||
ADDQ 0(msg), h0; \
|
||||
ADCQ 8(msg), h1; \
|
||||
ADCQ $1, h2; \
|
||||
LEAQ 16(msg), msg
|
||||
|
||||
#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3) \
|
||||
MOVQ r0, AX; \
|
||||
MULQ h0; \
|
||||
MOVQ AX, t0; \
|
||||
MOVQ DX, t1; \
|
||||
MOVQ r0, AX; \
|
||||
MULQ h1; \
|
||||
ADDQ AX, t1; \
|
||||
ADCQ $0, DX; \
|
||||
MOVQ r0, t2; \
|
||||
IMULQ h2, t2; \
|
||||
ADDQ DX, t2; \
|
||||
\
|
||||
MOVQ r1, AX; \
|
||||
MULQ h0; \
|
||||
ADDQ AX, t1; \
|
||||
ADCQ $0, DX; \
|
||||
MOVQ DX, h0; \
|
||||
MOVQ r1, t3; \
|
||||
IMULQ h2, t3; \
|
||||
MOVQ r1, AX; \
|
||||
MULQ h1; \
|
||||
ADDQ AX, t2; \
|
||||
ADCQ DX, t3; \
|
||||
ADDQ h0, t2; \
|
||||
ADCQ $0, t3; \
|
||||
\
|
||||
MOVQ t0, h0; \
|
||||
MOVQ t1, h1; \
|
||||
MOVQ t2, h2; \
|
||||
ANDQ $3, h2; \
|
||||
MOVQ t2, t0; \
|
||||
ANDQ $0xFFFFFFFFFFFFFFFC, t0; \
|
||||
ADDQ t0, h0; \
|
||||
ADCQ t3, h1; \
|
||||
ADCQ $0, h2; \
|
||||
SHRQ $2, t3, t2; \
|
||||
SHRQ $2, t3; \
|
||||
ADDQ t2, h0; \
|
||||
ADCQ t3, h1; \
|
||||
ADCQ $0, h2
|
||||
|
||||
// func update(state *[7]uint64, msg []byte)
|
||||
// func update(state *macState, msg []byte)
|
||||
TEXT ·update(SB), $0-32
|
||||
MOVQ state+0(FP), DI
|
||||
MOVQ msg_base+8(FP), SI
|
||||
MOVQ msg_len+16(FP), R15
|
||||
|
||||
MOVQ 0(DI), R8 // h0
|
||||
MOVQ 8(DI), R9 // h1
|
||||
MOVQ 16(DI), R10 // h2
|
||||
MOVQ 24(DI), R11 // r0
|
||||
MOVQ 32(DI), R12 // r1
|
||||
|
||||
CMPQ R15, $16
|
||||
MOVQ (DI), R8
|
||||
MOVQ 8(DI), R9
|
||||
MOVQ 16(DI), R10
|
||||
MOVQ 24(DI), R11
|
||||
MOVQ 32(DI), R12
|
||||
CMPQ R15, $0x10
|
||||
JB bytes_between_0_and_15
|
||||
|
||||
loop:
|
||||
POLY1305_ADD(SI, R8, R9, R10)
|
||||
ADDQ (SI), R8
|
||||
ADCQ 8(SI), R9
|
||||
ADCQ $0x01, R10
|
||||
LEAQ 16(SI), SI
|
||||
|
||||
multiply:
|
||||
POLY1305_MUL(R8, R9, R10, R11, R12, BX, CX, R13, R14)
|
||||
SUBQ $16, R15
|
||||
CMPQ R15, $16
|
||||
JAE loop
|
||||
MOVQ R11, AX
|
||||
MULQ R8
|
||||
MOVQ AX, BX
|
||||
MOVQ DX, CX
|
||||
MOVQ R11, AX
|
||||
MULQ R9
|
||||
ADDQ AX, CX
|
||||
ADCQ $0x00, DX
|
||||
MOVQ R11, R13
|
||||
IMULQ R10, R13
|
||||
ADDQ DX, R13
|
||||
MOVQ R12, AX
|
||||
MULQ R8
|
||||
ADDQ AX, CX
|
||||
ADCQ $0x00, DX
|
||||
MOVQ DX, R8
|
||||
MOVQ R12, R14
|
||||
IMULQ R10, R14
|
||||
MOVQ R12, AX
|
||||
MULQ R9
|
||||
ADDQ AX, R13
|
||||
ADCQ DX, R14
|
||||
ADDQ R8, R13
|
||||
ADCQ $0x00, R14
|
||||
MOVQ BX, R8
|
||||
MOVQ CX, R9
|
||||
MOVQ R13, R10
|
||||
ANDQ $0x03, R10
|
||||
MOVQ R13, BX
|
||||
ANDQ $-4, BX
|
||||
ADDQ BX, R8
|
||||
ADCQ R14, R9
|
||||
ADCQ $0x00, R10
|
||||
SHRQ $0x02, R14, R13
|
||||
SHRQ $0x02, R14
|
||||
ADDQ R13, R8
|
||||
ADCQ R14, R9
|
||||
ADCQ $0x00, R10
|
||||
SUBQ $0x10, R15
|
||||
CMPQ R15, $0x10
|
||||
JAE loop
|
||||
|
||||
bytes_between_0_and_15:
|
||||
TESTQ R15, R15
|
||||
JZ done
|
||||
MOVQ $1, BX
|
||||
MOVQ $0x00000001, BX
|
||||
XORQ CX, CX
|
||||
XORQ R13, R13
|
||||
ADDQ R15, SI
|
||||
|
||||
flush_buffer:
|
||||
SHLQ $8, BX, CX
|
||||
SHLQ $8, BX
|
||||
SHLQ $0x08, BX, CX
|
||||
SHLQ $0x08, BX
|
||||
MOVB -1(SI), R13
|
||||
XORQ R13, BX
|
||||
DECQ SI
|
||||
DECQ R15
|
||||
JNZ flush_buffer
|
||||
|
||||
ADDQ BX, R8
|
||||
ADCQ CX, R9
|
||||
ADCQ $0, R10
|
||||
MOVQ $16, R15
|
||||
ADCQ $0x00, R10
|
||||
MOVQ $0x00000010, R15
|
||||
JMP multiply
|
||||
|
||||
done:
|
||||
MOVQ R8, 0(DI)
|
||||
MOVQ R8, (DI)
|
||||
MOVQ R9, 8(DI)
|
||||
MOVQ R10, 16(DI)
|
||||
RET
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
//go:build gc && !purego && (amd64 || loong64 || ppc64 || ppc64le)
|
||||
|
||||
package poly1305
|
||||
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
// Copyright 2025 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
|
||||
// func update(state *macState, msg []byte)
|
||||
TEXT ·update(SB), $0-32
|
||||
MOVV state+0(FP), R4
|
||||
MOVV msg_base+8(FP), R5
|
||||
MOVV msg_len+16(FP), R6
|
||||
|
||||
MOVV $0x10, R7
|
||||
|
||||
MOVV (R4), R8 // h0
|
||||
MOVV 8(R4), R9 // h1
|
||||
MOVV 16(R4), R10 // h2
|
||||
MOVV 24(R4), R11 // r0
|
||||
MOVV 32(R4), R12 // r1
|
||||
|
||||
BLT R6, R7, bytes_between_0_and_15
|
||||
|
||||
loop:
|
||||
MOVV (R5), R14 // msg[0:8]
|
||||
MOVV 8(R5), R16 // msg[8:16]
|
||||
ADDV R14, R8, R8 // h0 (x1 + y1 = z1', if z1' < x1 then z1' overflow)
|
||||
ADDV R16, R9, R27
|
||||
SGTU R14, R8, R24 // h0.carry
|
||||
SGTU R9, R27, R28
|
||||
ADDV R27, R24, R9 // h1
|
||||
SGTU R27, R9, R24
|
||||
OR R24, R28, R24 // h1.carry
|
||||
ADDV $0x01, R24, R24
|
||||
ADDV R10, R24, R10 // h2
|
||||
|
||||
ADDV $16, R5, R5 // msg = msg[16:]
|
||||
|
||||
multiply:
|
||||
MULV R8, R11, R14 // h0r0.lo
|
||||
MULHVU R8, R11, R15 // h0r0.hi
|
||||
MULV R9, R11, R13 // h1r0.lo
|
||||
MULHVU R9, R11, R16 // h1r0.hi
|
||||
ADDV R13, R15, R15
|
||||
SGTU R13, R15, R24
|
||||
ADDV R24, R16, R16
|
||||
MULV R10, R11, R25
|
||||
ADDV R16, R25, R25
|
||||
MULV R8, R12, R13 // h0r1.lo
|
||||
MULHVU R8, R12, R16 // h0r1.hi
|
||||
ADDV R13, R15, R15
|
||||
SGTU R13, R15, R24
|
||||
ADDV R24, R16, R16
|
||||
MOVV R16, R8
|
||||
MULV R10, R12, R26 // h2r1
|
||||
MULV R9, R12, R13 // h1r1.lo
|
||||
MULHVU R9, R12, R16 // h1r1.hi
|
||||
ADDV R13, R25, R25
|
||||
ADDV R16, R26, R27
|
||||
SGTU R13, R25, R24
|
||||
ADDV R27, R24, R26
|
||||
ADDV R8, R25, R25
|
||||
SGTU R8, R25, R24
|
||||
ADDV R24, R26, R26
|
||||
AND $3, R25, R10
|
||||
AND $-4, R25, R17
|
||||
ADDV R17, R14, R8
|
||||
ADDV R26, R15, R27
|
||||
SGTU R17, R8, R24
|
||||
SGTU R26, R27, R28
|
||||
ADDV R27, R24, R9
|
||||
SGTU R27, R9, R24
|
||||
OR R24, R28, R24
|
||||
ADDV R24, R10, R10
|
||||
SLLV $62, R26, R27
|
||||
SRLV $2, R25, R28
|
||||
SRLV $2, R26, R26
|
||||
OR R27, R28, R25
|
||||
ADDV R25, R8, R8
|
||||
ADDV R26, R9, R27
|
||||
SGTU R25, R8, R24
|
||||
SGTU R26, R27, R28
|
||||
ADDV R27, R24, R9
|
||||
SGTU R27, R9, R24
|
||||
OR R24, R28, R24
|
||||
ADDV R24, R10, R10
|
||||
|
||||
SUBV $16, R6, R6
|
||||
BGE R6, R7, loop
|
||||
|
||||
bytes_between_0_and_15:
|
||||
BEQ R6, R0, done
|
||||
MOVV $1, R14
|
||||
XOR R15, R15
|
||||
ADDV R6, R5, R5
|
||||
|
||||
flush_buffer:
|
||||
MOVBU -1(R5), R25
|
||||
SRLV $56, R14, R24
|
||||
SLLV $8, R15, R28
|
||||
SLLV $8, R14, R14
|
||||
OR R24, R28, R15
|
||||
XOR R25, R14, R14
|
||||
SUBV $1, R6, R6
|
||||
SUBV $1, R5, R5
|
||||
BNE R6, R0, flush_buffer
|
||||
|
||||
ADDV R14, R8, R8
|
||||
SGTU R14, R8, R24
|
||||
ADDV R15, R9, R27
|
||||
SGTU R15, R27, R28
|
||||
ADDV R27, R24, R9
|
||||
SGTU R27, R9, R24
|
||||
OR R24, R28, R24
|
||||
ADDV R10, R24, R10
|
||||
|
||||
MOVV $16, R6
|
||||
JMP multiply
|
||||
|
||||
done:
|
||||
MOVV R8, (R4)
|
||||
MOVV R9, 8(R4)
|
||||
MOVV R10, 16(R4)
|
||||
RET
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
|
||||
package poly1305
|
||||
|
||||
//go:noescape
|
||||
func update(state *macState, msg []byte)
|
||||
|
||||
// mac is a wrapper for macGeneric that redirects calls that would have gone to
|
||||
// updateGeneric to update.
|
||||
//
|
||||
// Its Write and Sum methods are otherwise identical to the macGeneric ones, but
|
||||
// using function pointers would carry a major performance cost.
|
||||
type mac struct{ macGeneric }
|
||||
|
||||
func (h *mac) Write(p []byte) (int, error) {
|
||||
nn := len(p)
|
||||
if h.offset > 0 {
|
||||
n := copy(h.buffer[h.offset:], p)
|
||||
if h.offset+n < TagSize {
|
||||
h.offset += n
|
||||
return nn, nil
|
||||
}
|
||||
p = p[n:]
|
||||
h.offset = 0
|
||||
update(&h.macState, h.buffer[:])
|
||||
}
|
||||
if n := len(p) - (len(p) % TagSize); n > 0 {
|
||||
update(&h.macState, p[:n])
|
||||
p = p[n:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
h.offset += copy(h.buffer[h.offset:], p)
|
||||
}
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
func (h *mac) Sum(out *[16]byte) {
|
||||
state := h.macState
|
||||
if h.offset > 0 {
|
||||
update(&state, h.buffer[:h.offset])
|
||||
}
|
||||
finalize(out, &state.h, &state.s)
|
||||
}
|
||||
Generated
Vendored
+19
-11
@@ -2,15 +2,25 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
//go:build gc && !purego && (ppc64 || ppc64le)
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// This was ported from the amd64 implementation.
|
||||
|
||||
#ifdef GOARCH_ppc64le
|
||||
#define LE_MOVD MOVD
|
||||
#define LE_MOVWZ MOVWZ
|
||||
#define LE_MOVHZ MOVHZ
|
||||
#else
|
||||
#define LE_MOVD MOVDBR
|
||||
#define LE_MOVWZ MOVWBR
|
||||
#define LE_MOVHZ MOVHBR
|
||||
#endif
|
||||
|
||||
#define POLY1305_ADD(msg, h0, h1, h2, t0, t1, t2) \
|
||||
MOVD (msg), t0; \
|
||||
MOVD 8(msg), t1; \
|
||||
LE_MOVD (msg)( R0), t0; \
|
||||
LE_MOVD (msg)(R24), t1; \
|
||||
MOVD $1, t2; \
|
||||
ADDC t0, h0, h0; \
|
||||
ADDE t1, h1, h1; \
|
||||
@@ -50,10 +60,6 @@
|
||||
ADDE t3, h1, h1; \
|
||||
ADDZE h2
|
||||
|
||||
DATA ·poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF
|
||||
DATA ·poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC
|
||||
GLOBL ·poly1305Mask<>(SB), RODATA, $16
|
||||
|
||||
// func update(state *[7]uint64, msg []byte)
|
||||
TEXT ·update(SB), $0-32
|
||||
MOVD state+0(FP), R3
|
||||
@@ -66,6 +72,8 @@ TEXT ·update(SB), $0-32
|
||||
MOVD 24(R3), R11 // r0
|
||||
MOVD 32(R3), R12 // r1
|
||||
|
||||
MOVD $8, R24
|
||||
|
||||
CMP R5, $16
|
||||
BLT bytes_between_0_and_15
|
||||
|
||||
@@ -94,7 +102,7 @@ flush_buffer:
|
||||
|
||||
// Greater than 8 -- load the rightmost remaining bytes in msg
|
||||
// and put into R17 (h1)
|
||||
MOVD (R4)(R21), R17
|
||||
LE_MOVD (R4)(R21), R17
|
||||
MOVD $16, R22
|
||||
|
||||
// Find the offset to those bytes
|
||||
@@ -118,7 +126,7 @@ just1:
|
||||
BLT less8
|
||||
|
||||
// Exactly 8
|
||||
MOVD (R4), R16
|
||||
LE_MOVD (R4), R16
|
||||
|
||||
CMP R17, $0
|
||||
|
||||
@@ -133,7 +141,7 @@ less8:
|
||||
MOVD $0, R22 // shift count
|
||||
CMP R5, $4
|
||||
BLT less4
|
||||
MOVWZ (R4), R16
|
||||
LE_MOVWZ (R4), R16
|
||||
ADD $4, R4
|
||||
ADD $-4, R5
|
||||
MOVD $32, R22
|
||||
@@ -141,7 +149,7 @@ less8:
|
||||
less4:
|
||||
CMP R5, $2
|
||||
BLT less2
|
||||
MOVHZ (R4), R21
|
||||
LE_MOVHZ (R4), R21
|
||||
SLD R22, R21, R21
|
||||
OR R16, R21, R16
|
||||
ADD $16, R22
|
||||
+2
-2
@@ -4,10 +4,10 @@
|
||||
|
||||
// Package md4 implements the MD4 hash algorithm as defined in RFC 1320.
|
||||
//
|
||||
// Deprecated: MD4 is cryptographically broken and should should only be used
|
||||
// Deprecated: MD4 is cryptographically broken and should only be used
|
||||
// where compatibility with legacy systems, not security, is the goal. Instead,
|
||||
// use a secure hash like SHA-256 (from crypto/sha256).
|
||||
package md4 // import "golang.org/x/crypto/md4"
|
||||
package md4
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ type pbeCipher interface {
|
||||
create(key []byte) (cipher.Block, error)
|
||||
// deriveKey returns a key derived from the given password and salt.
|
||||
deriveKey(salt, password []byte, iterations int) []byte
|
||||
// deriveKey returns an IV derived from the given password and salt.
|
||||
// deriveIV returns an IV derived from the given password and salt.
|
||||
deriveIV(salt, password []byte, iterations int) []byte
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
// Deprecated: RIPEMD-160 is a legacy hash and should not be used for new
|
||||
// applications. Also, this package does not and will not provide an optimized
|
||||
// implementation. Instead, use a modern hash like SHA-256 (from crypto/sha256).
|
||||
package ripemd160 // import "golang.org/x/crypto/ripemd160"
|
||||
package ripemd160
|
||||
|
||||
// RIPEMD-160 is designed by Hans Dobbertin, Antoon Bosselaers, and Bart
|
||||
// Preneel with specifications available at:
|
||||
|
||||
+5
-1
@@ -5,6 +5,10 @@
|
||||
// Package sha3 implements the SHA-3 fixed-output-length hash functions and
|
||||
// the SHAKE variable-output-length hash functions defined by FIPS-202.
|
||||
//
|
||||
// All types in this package also implement [encoding.BinaryMarshaler],
|
||||
// [encoding.BinaryAppender] and [encoding.BinaryUnmarshaler] to marshal and
|
||||
// unmarshal the internal state of the hash.
|
||||
//
|
||||
// Both types of hash function use the "sponge" construction and the Keccak
|
||||
// permutation. For a detailed specification see http://keccak.noekeon.org/
|
||||
//
|
||||
@@ -59,4 +63,4 @@
|
||||
// They produce output of the same length, with the same security strengths
|
||||
// against all attacks. This means, in particular, that SHA3-256 only has
|
||||
// 128-bit collision resistance, because its output length is 32 bytes.
|
||||
package sha3 // import "golang.org/x/crypto/sha3"
|
||||
package sha3
|
||||
|
||||
+49
-18
@@ -9,6 +9,7 @@ package sha3
|
||||
// bytes.
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"hash"
|
||||
)
|
||||
|
||||
@@ -16,53 +17,83 @@ import (
|
||||
// Its generic security strength is 224 bits against preimage attacks,
|
||||
// and 112 bits against collision attacks.
|
||||
func New224() hash.Hash {
|
||||
if h := new224Asm(); h != nil {
|
||||
return h
|
||||
}
|
||||
return &state{rate: 144, outputLen: 28, dsbyte: 0x06}
|
||||
return new224()
|
||||
}
|
||||
|
||||
// New256 creates a new SHA3-256 hash.
|
||||
// Its generic security strength is 256 bits against preimage attacks,
|
||||
// and 128 bits against collision attacks.
|
||||
func New256() hash.Hash {
|
||||
if h := new256Asm(); h != nil {
|
||||
return h
|
||||
}
|
||||
return &state{rate: 136, outputLen: 32, dsbyte: 0x06}
|
||||
return new256()
|
||||
}
|
||||
|
||||
// New384 creates a new SHA3-384 hash.
|
||||
// Its generic security strength is 384 bits against preimage attacks,
|
||||
// and 192 bits against collision attacks.
|
||||
func New384() hash.Hash {
|
||||
if h := new384Asm(); h != nil {
|
||||
return h
|
||||
}
|
||||
return &state{rate: 104, outputLen: 48, dsbyte: 0x06}
|
||||
return new384()
|
||||
}
|
||||
|
||||
// New512 creates a new SHA3-512 hash.
|
||||
// Its generic security strength is 512 bits against preimage attacks,
|
||||
// and 256 bits against collision attacks.
|
||||
func New512() hash.Hash {
|
||||
if h := new512Asm(); h != nil {
|
||||
return h
|
||||
}
|
||||
return &state{rate: 72, outputLen: 64, dsbyte: 0x06}
|
||||
return new512()
|
||||
}
|
||||
|
||||
func init() {
|
||||
crypto.RegisterHash(crypto.SHA3_224, New224)
|
||||
crypto.RegisterHash(crypto.SHA3_256, New256)
|
||||
crypto.RegisterHash(crypto.SHA3_384, New384)
|
||||
crypto.RegisterHash(crypto.SHA3_512, New512)
|
||||
}
|
||||
|
||||
const (
|
||||
dsbyteSHA3 = 0b00000110
|
||||
dsbyteKeccak = 0b00000001
|
||||
dsbyteShake = 0b00011111
|
||||
dsbyteCShake = 0b00000100
|
||||
|
||||
// rateK[c] is the rate in bytes for Keccak[c] where c is the capacity in
|
||||
// bits. Given the sponge size is 1600 bits, the rate is 1600 - c bits.
|
||||
rateK256 = (1600 - 256) / 8
|
||||
rateK448 = (1600 - 448) / 8
|
||||
rateK512 = (1600 - 512) / 8
|
||||
rateK768 = (1600 - 768) / 8
|
||||
rateK1024 = (1600 - 1024) / 8
|
||||
)
|
||||
|
||||
func new224Generic() *state {
|
||||
return &state{rate: rateK448, outputLen: 28, dsbyte: dsbyteSHA3}
|
||||
}
|
||||
|
||||
func new256Generic() *state {
|
||||
return &state{rate: rateK512, outputLen: 32, dsbyte: dsbyteSHA3}
|
||||
}
|
||||
|
||||
func new384Generic() *state {
|
||||
return &state{rate: rateK768, outputLen: 48, dsbyte: dsbyteSHA3}
|
||||
}
|
||||
|
||||
func new512Generic() *state {
|
||||
return &state{rate: rateK1024, outputLen: 64, dsbyte: dsbyteSHA3}
|
||||
}
|
||||
|
||||
// NewLegacyKeccak256 creates a new Keccak-256 hash.
|
||||
//
|
||||
// Only use this function if you require compatibility with an existing cryptosystem
|
||||
// that uses non-standard padding. All other users should use New256 instead.
|
||||
func NewLegacyKeccak256() hash.Hash { return &state{rate: 136, outputLen: 32, dsbyte: 0x01} }
|
||||
func NewLegacyKeccak256() hash.Hash {
|
||||
return &state{rate: rateK512, outputLen: 32, dsbyte: dsbyteKeccak}
|
||||
}
|
||||
|
||||
// NewLegacyKeccak512 creates a new Keccak-512 hash.
|
||||
//
|
||||
// Only use this function if you require compatibility with an existing cryptosystem
|
||||
// that uses non-standard padding. All other users should use New512 instead.
|
||||
func NewLegacyKeccak512() hash.Hash { return &state{rate: 72, outputLen: 64, dsbyte: 0x01} }
|
||||
func NewLegacyKeccak512() hash.Hash {
|
||||
return &state{rate: rateK1024, outputLen: 64, dsbyte: dsbyteKeccak}
|
||||
}
|
||||
|
||||
// Sum224 returns the SHA3-224 digest of the data.
|
||||
func Sum224(data []byte) (digest [28]byte) {
|
||||
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !gc || purego || !s390x
|
||||
|
||||
package sha3
|
||||
|
||||
import (
|
||||
"hash"
|
||||
)
|
||||
|
||||
// new224Asm returns an assembly implementation of SHA3-224 if available,
|
||||
// otherwise it returns nil.
|
||||
func new224Asm() hash.Hash { return nil }
|
||||
|
||||
// new256Asm returns an assembly implementation of SHA3-256 if available,
|
||||
// otherwise it returns nil.
|
||||
func new256Asm() hash.Hash { return nil }
|
||||
|
||||
// new384Asm returns an assembly implementation of SHA3-384 if available,
|
||||
// otherwise it returns nil.
|
||||
func new384Asm() hash.Hash { return nil }
|
||||
|
||||
// new512Asm returns an assembly implementation of SHA3-512 if available,
|
||||
// otherwise it returns nil.
|
||||
func new512Asm() hash.Hash { return nil }
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !gc || purego || !s390x
|
||||
|
||||
package sha3
|
||||
|
||||
func new224() *state {
|
||||
return new224Generic()
|
||||
}
|
||||
|
||||
func new256() *state {
|
||||
return new256Generic()
|
||||
}
|
||||
|
||||
func new384() *state {
|
||||
return new384Generic()
|
||||
}
|
||||
|
||||
func new512() *state {
|
||||
return new512Generic()
|
||||
}
|
||||
+5403
-374
File diff suppressed because it is too large
Load Diff
-18
@@ -1,18 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.4
|
||||
|
||||
package sha3
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
)
|
||||
|
||||
func init() {
|
||||
crypto.RegisterHash(crypto.SHA3_224, New224)
|
||||
crypto.RegisterHash(crypto.SHA3_256, New256)
|
||||
crypto.RegisterHash(crypto.SHA3_384, New384)
|
||||
crypto.RegisterHash(crypto.SHA3_512, New512)
|
||||
}
|
||||
+125
-78
@@ -4,6 +4,15 @@
|
||||
|
||||
package sha3
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
// spongeDirection indicates the direction bytes are flowing through the sponge.
|
||||
type spongeDirection int
|
||||
|
||||
@@ -14,17 +23,13 @@ const (
|
||||
spongeSqueezing
|
||||
)
|
||||
|
||||
const (
|
||||
// maxRate is the maximum size of the internal buffer. SHAKE-256
|
||||
// currently needs the largest buffer.
|
||||
maxRate = 168
|
||||
)
|
||||
|
||||
type state struct {
|
||||
// Generic sponge components.
|
||||
a [25]uint64 // main state of the hash
|
||||
buf []byte // points into storage
|
||||
rate int // the number of bytes of state to use
|
||||
a [1600 / 8]byte // main state of the hash
|
||||
|
||||
// a[n:rate] is the buffer. If absorbing, it's the remaining space to XOR
|
||||
// into before running the permutation. If squeezing, it's the remaining
|
||||
// output to produce before running the permutation.
|
||||
n, rate int
|
||||
|
||||
// dsbyte contains the "domain separation" bits and the first bit of
|
||||
// the padding. Sections 6.1 and 6.2 of [1] separate the outputs of the
|
||||
@@ -40,9 +45,6 @@ type state struct {
|
||||
// Extendable-Output Functions (May 2014)"
|
||||
dsbyte byte
|
||||
|
||||
storage storageBuf
|
||||
|
||||
// Specific to SHA-3 and SHAKE.
|
||||
outputLen int // the default output size in bytes
|
||||
state spongeDirection // whether the sponge is absorbing or squeezing
|
||||
}
|
||||
@@ -54,103 +56,77 @@ func (d *state) BlockSize() int { return d.rate }
|
||||
func (d *state) Size() int { return d.outputLen }
|
||||
|
||||
// Reset clears the internal state by zeroing the sponge state and
|
||||
// the byte buffer, and setting Sponge.state to absorbing.
|
||||
// the buffer indexes, and setting Sponge.state to absorbing.
|
||||
func (d *state) Reset() {
|
||||
// Zero the permutation's state.
|
||||
for i := range d.a {
|
||||
d.a[i] = 0
|
||||
}
|
||||
d.state = spongeAbsorbing
|
||||
d.buf = d.storage.asBytes()[:0]
|
||||
d.n = 0
|
||||
}
|
||||
|
||||
func (d *state) clone() *state {
|
||||
ret := *d
|
||||
if ret.state == spongeAbsorbing {
|
||||
ret.buf = ret.storage.asBytes()[:len(ret.buf)]
|
||||
} else {
|
||||
ret.buf = ret.storage.asBytes()[d.rate-cap(d.buf) : d.rate]
|
||||
}
|
||||
|
||||
return &ret
|
||||
}
|
||||
|
||||
// permute applies the KeccakF-1600 permutation. It handles
|
||||
// any input-output buffering.
|
||||
// permute applies the KeccakF-1600 permutation.
|
||||
func (d *state) permute() {
|
||||
switch d.state {
|
||||
case spongeAbsorbing:
|
||||
// If we're absorbing, we need to xor the input into the state
|
||||
// before applying the permutation.
|
||||
xorIn(d, d.buf)
|
||||
d.buf = d.storage.asBytes()[:0]
|
||||
keccakF1600(&d.a)
|
||||
case spongeSqueezing:
|
||||
// If we're squeezing, we need to apply the permutation before
|
||||
// copying more output.
|
||||
keccakF1600(&d.a)
|
||||
d.buf = d.storage.asBytes()[:d.rate]
|
||||
copyOut(d, d.buf)
|
||||
var a *[25]uint64
|
||||
if cpu.IsBigEndian {
|
||||
a = new([25]uint64)
|
||||
for i := range a {
|
||||
a[i] = binary.LittleEndian.Uint64(d.a[i*8:])
|
||||
}
|
||||
} else {
|
||||
a = (*[25]uint64)(unsafe.Pointer(&d.a))
|
||||
}
|
||||
|
||||
keccakF1600(a)
|
||||
d.n = 0
|
||||
|
||||
if cpu.IsBigEndian {
|
||||
for i := range a {
|
||||
binary.LittleEndian.PutUint64(d.a[i*8:], a[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pads appends the domain separation bits in dsbyte, applies
|
||||
// the multi-bitrate 10..1 padding rule, and permutes the state.
|
||||
func (d *state) padAndPermute(dsbyte byte) {
|
||||
if d.buf == nil {
|
||||
d.buf = d.storage.asBytes()[:0]
|
||||
}
|
||||
func (d *state) padAndPermute() {
|
||||
// Pad with this instance's domain-separator bits. We know that there's
|
||||
// at least one byte of space in d.buf because, if it were full,
|
||||
// at least one byte of space in the sponge because, if it were full,
|
||||
// permute would have been called to empty it. dsbyte also contains the
|
||||
// first one bit for the padding. See the comment in the state struct.
|
||||
d.buf = append(d.buf, dsbyte)
|
||||
zerosStart := len(d.buf)
|
||||
d.buf = d.storage.asBytes()[:d.rate]
|
||||
for i := zerosStart; i < d.rate; i++ {
|
||||
d.buf[i] = 0
|
||||
}
|
||||
d.a[d.n] ^= d.dsbyte
|
||||
// This adds the final one bit for the padding. Because of the way that
|
||||
// bits are numbered from the LSB upwards, the final bit is the MSB of
|
||||
// the last byte.
|
||||
d.buf[d.rate-1] ^= 0x80
|
||||
d.a[d.rate-1] ^= 0x80
|
||||
// Apply the permutation
|
||||
d.permute()
|
||||
d.state = spongeSqueezing
|
||||
d.buf = d.storage.asBytes()[:d.rate]
|
||||
copyOut(d, d.buf)
|
||||
}
|
||||
|
||||
// Write absorbs more data into the hash's state. It panics if any
|
||||
// output has already been read.
|
||||
func (d *state) Write(p []byte) (written int, err error) {
|
||||
func (d *state) Write(p []byte) (n int, err error) {
|
||||
if d.state != spongeAbsorbing {
|
||||
panic("sha3: Write after Read")
|
||||
}
|
||||
if d.buf == nil {
|
||||
d.buf = d.storage.asBytes()[:0]
|
||||
}
|
||||
written = len(p)
|
||||
|
||||
n = len(p)
|
||||
|
||||
for len(p) > 0 {
|
||||
if len(d.buf) == 0 && len(p) >= d.rate {
|
||||
// The fast path; absorb a full "rate" bytes of input and apply the permutation.
|
||||
xorIn(d, p[:d.rate])
|
||||
p = p[d.rate:]
|
||||
keccakF1600(&d.a)
|
||||
} else {
|
||||
// The slow path; buffer the input until we can fill the sponge, and then xor it in.
|
||||
todo := d.rate - len(d.buf)
|
||||
if todo > len(p) {
|
||||
todo = len(p)
|
||||
}
|
||||
d.buf = append(d.buf, p[:todo]...)
|
||||
p = p[todo:]
|
||||
x := subtle.XORBytes(d.a[d.n:d.rate], d.a[d.n:d.rate], p)
|
||||
d.n += x
|
||||
p = p[x:]
|
||||
|
||||
// If the sponge is full, apply the permutation.
|
||||
if len(d.buf) == d.rate {
|
||||
d.permute()
|
||||
}
|
||||
// If the sponge is full, apply the permutation.
|
||||
if d.n == d.rate {
|
||||
d.permute()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,21 +137,21 @@ func (d *state) Write(p []byte) (written int, err error) {
|
||||
func (d *state) Read(out []byte) (n int, err error) {
|
||||
// If we're still absorbing, pad and apply the permutation.
|
||||
if d.state == spongeAbsorbing {
|
||||
d.padAndPermute(d.dsbyte)
|
||||
d.padAndPermute()
|
||||
}
|
||||
|
||||
n = len(out)
|
||||
|
||||
// Now, do the squeezing.
|
||||
for len(out) > 0 {
|
||||
n := copy(out, d.buf)
|
||||
d.buf = d.buf[n:]
|
||||
out = out[n:]
|
||||
|
||||
// Apply the permutation if we've squeezed the sponge dry.
|
||||
if len(d.buf) == 0 {
|
||||
if d.n == d.rate {
|
||||
d.permute()
|
||||
}
|
||||
|
||||
x := copy(out, d.a[d.n:d.rate])
|
||||
d.n += x
|
||||
out = out[x:]
|
||||
}
|
||||
|
||||
return
|
||||
@@ -195,3 +171,74 @@ func (d *state) Sum(in []byte) []byte {
|
||||
dup.Read(hash)
|
||||
return append(in, hash...)
|
||||
}
|
||||
|
||||
const (
|
||||
magicSHA3 = "sha\x08"
|
||||
magicShake = "sha\x09"
|
||||
magicCShake = "sha\x0a"
|
||||
magicKeccak = "sha\x0b"
|
||||
// magic || rate || main state || n || sponge direction
|
||||
marshaledSize = len(magicSHA3) + 1 + 200 + 1 + 1
|
||||
)
|
||||
|
||||
func (d *state) MarshalBinary() ([]byte, error) {
|
||||
return d.AppendBinary(make([]byte, 0, marshaledSize))
|
||||
}
|
||||
|
||||
func (d *state) AppendBinary(b []byte) ([]byte, error) {
|
||||
switch d.dsbyte {
|
||||
case dsbyteSHA3:
|
||||
b = append(b, magicSHA3...)
|
||||
case dsbyteShake:
|
||||
b = append(b, magicShake...)
|
||||
case dsbyteCShake:
|
||||
b = append(b, magicCShake...)
|
||||
case dsbyteKeccak:
|
||||
b = append(b, magicKeccak...)
|
||||
default:
|
||||
panic("unknown dsbyte")
|
||||
}
|
||||
// rate is at most 168, and n is at most rate.
|
||||
b = append(b, byte(d.rate))
|
||||
b = append(b, d.a[:]...)
|
||||
b = append(b, byte(d.n), byte(d.state))
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (d *state) UnmarshalBinary(b []byte) error {
|
||||
if len(b) != marshaledSize {
|
||||
return errors.New("sha3: invalid hash state")
|
||||
}
|
||||
|
||||
magic := string(b[:len(magicSHA3)])
|
||||
b = b[len(magicSHA3):]
|
||||
switch {
|
||||
case magic == magicSHA3 && d.dsbyte == dsbyteSHA3:
|
||||
case magic == magicShake && d.dsbyte == dsbyteShake:
|
||||
case magic == magicCShake && d.dsbyte == dsbyteCShake:
|
||||
case magic == magicKeccak && d.dsbyte == dsbyteKeccak:
|
||||
default:
|
||||
return errors.New("sha3: invalid hash state identifier")
|
||||
}
|
||||
|
||||
rate := int(b[0])
|
||||
b = b[1:]
|
||||
if rate != d.rate {
|
||||
return errors.New("sha3: invalid hash state function")
|
||||
}
|
||||
|
||||
copy(d.a[:], b)
|
||||
b = b[len(d.a):]
|
||||
|
||||
n, state := int(b[0]), spongeDirection(b[1])
|
||||
if n > d.rate {
|
||||
return errors.New("sha3: invalid hash state")
|
||||
}
|
||||
d.n = n
|
||||
if state != spongeAbsorbing && state != spongeSqueezing {
|
||||
return errors.New("sha3: invalid hash state")
|
||||
}
|
||||
d.state = state
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+41
-26
@@ -143,6 +143,12 @@ func (s *asmState) Write(b []byte) (int, error) {
|
||||
|
||||
// Read squeezes an arbitrary number of bytes from the sponge.
|
||||
func (s *asmState) Read(out []byte) (n int, err error) {
|
||||
// The 'compute last message digest' instruction only stores the digest
|
||||
// at the first operand (dst) for SHAKE functions.
|
||||
if s.function != shake_128 && s.function != shake_256 {
|
||||
panic("sha3: can only call Read for SHAKE functions")
|
||||
}
|
||||
|
||||
n = len(out)
|
||||
|
||||
// need to pad if we were absorbing
|
||||
@@ -202,8 +208,17 @@ func (s *asmState) Sum(b []byte) []byte {
|
||||
|
||||
// Hash the buffer. Note that we don't clear it because we
|
||||
// aren't updating the state.
|
||||
klmd(s.function, &a, nil, s.buf)
|
||||
return append(b, a[:s.outputLen]...)
|
||||
switch s.function {
|
||||
case sha3_224, sha3_256, sha3_384, sha3_512:
|
||||
klmd(s.function, &a, nil, s.buf)
|
||||
return append(b, a[:s.outputLen]...)
|
||||
case shake_128, shake_256:
|
||||
d := make([]byte, s.outputLen, 64)
|
||||
klmd(s.function, &a, d, s.buf)
|
||||
return append(b, d[:s.outputLen]...)
|
||||
default:
|
||||
panic("sha3: unknown function")
|
||||
}
|
||||
}
|
||||
|
||||
// Reset resets the Hash to its initial state.
|
||||
@@ -233,56 +248,56 @@ func (s *asmState) Clone() ShakeHash {
|
||||
return s.clone()
|
||||
}
|
||||
|
||||
// new224Asm returns an assembly implementation of SHA3-224 if available,
|
||||
// otherwise it returns nil.
|
||||
func new224Asm() hash.Hash {
|
||||
// new224 returns an assembly implementation of SHA3-224 if available,
|
||||
// otherwise it returns a generic implementation.
|
||||
func new224() hash.Hash {
|
||||
if cpu.S390X.HasSHA3 {
|
||||
return newAsmState(sha3_224)
|
||||
}
|
||||
return nil
|
||||
return new224Generic()
|
||||
}
|
||||
|
||||
// new256Asm returns an assembly implementation of SHA3-256 if available,
|
||||
// otherwise it returns nil.
|
||||
func new256Asm() hash.Hash {
|
||||
// new256 returns an assembly implementation of SHA3-256 if available,
|
||||
// otherwise it returns a generic implementation.
|
||||
func new256() hash.Hash {
|
||||
if cpu.S390X.HasSHA3 {
|
||||
return newAsmState(sha3_256)
|
||||
}
|
||||
return nil
|
||||
return new256Generic()
|
||||
}
|
||||
|
||||
// new384Asm returns an assembly implementation of SHA3-384 if available,
|
||||
// otherwise it returns nil.
|
||||
func new384Asm() hash.Hash {
|
||||
// new384 returns an assembly implementation of SHA3-384 if available,
|
||||
// otherwise it returns a generic implementation.
|
||||
func new384() hash.Hash {
|
||||
if cpu.S390X.HasSHA3 {
|
||||
return newAsmState(sha3_384)
|
||||
}
|
||||
return nil
|
||||
return new384Generic()
|
||||
}
|
||||
|
||||
// new512Asm returns an assembly implementation of SHA3-512 if available,
|
||||
// otherwise it returns nil.
|
||||
func new512Asm() hash.Hash {
|
||||
// new512 returns an assembly implementation of SHA3-512 if available,
|
||||
// otherwise it returns a generic implementation.
|
||||
func new512() hash.Hash {
|
||||
if cpu.S390X.HasSHA3 {
|
||||
return newAsmState(sha3_512)
|
||||
}
|
||||
return nil
|
||||
return new512Generic()
|
||||
}
|
||||
|
||||
// newShake128Asm returns an assembly implementation of SHAKE-128 if available,
|
||||
// otherwise it returns nil.
|
||||
func newShake128Asm() ShakeHash {
|
||||
// newShake128 returns an assembly implementation of SHAKE-128 if available,
|
||||
// otherwise it returns a generic implementation.
|
||||
func newShake128() ShakeHash {
|
||||
if cpu.S390X.HasSHA3 {
|
||||
return newAsmState(shake_128)
|
||||
}
|
||||
return nil
|
||||
return newShake128Generic()
|
||||
}
|
||||
|
||||
// newShake256Asm returns an assembly implementation of SHAKE-256 if available,
|
||||
// otherwise it returns nil.
|
||||
func newShake256Asm() ShakeHash {
|
||||
// newShake256 returns an assembly implementation of SHAKE-256 if available,
|
||||
// otherwise it returns a generic implementation.
|
||||
func newShake256() ShakeHash {
|
||||
if cpu.S390X.HasSHA3 {
|
||||
return newAsmState(shake_256)
|
||||
}
|
||||
return nil
|
||||
return newShake256Generic()
|
||||
}
|
||||
|
||||
+61
-40
@@ -16,9 +16,12 @@ package sha3
|
||||
// [2] https://doi.org/10.6028/NIST.SP.800-185
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"hash"
|
||||
"io"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// ShakeHash defines the interface to hash functions that support
|
||||
@@ -50,44 +53,36 @@ type cshakeState struct {
|
||||
initBlock []byte
|
||||
}
|
||||
|
||||
// Consts for configuring initial SHA-3 state
|
||||
const (
|
||||
dsbyteShake = 0x1f
|
||||
dsbyteCShake = 0x04
|
||||
rate128 = 168
|
||||
rate256 = 136
|
||||
)
|
||||
|
||||
func bytepad(input []byte, w int) []byte {
|
||||
// leftEncode always returns max 9 bytes
|
||||
buf := make([]byte, 0, 9+len(input)+w)
|
||||
buf = append(buf, leftEncode(uint64(w))...)
|
||||
buf = append(buf, input...)
|
||||
padlen := w - (len(buf) % w)
|
||||
return append(buf, make([]byte, padlen)...)
|
||||
func bytepad(data []byte, rate int) []byte {
|
||||
out := make([]byte, 0, 9+len(data)+rate-1)
|
||||
out = append(out, leftEncode(uint64(rate))...)
|
||||
out = append(out, data...)
|
||||
if padlen := rate - len(out)%rate; padlen < rate {
|
||||
out = append(out, make([]byte, padlen)...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func leftEncode(value uint64) []byte {
|
||||
var b [9]byte
|
||||
binary.BigEndian.PutUint64(b[1:], value)
|
||||
// Trim all but last leading zero bytes
|
||||
i := byte(1)
|
||||
for i < 8 && b[i] == 0 {
|
||||
i++
|
||||
func leftEncode(x uint64) []byte {
|
||||
// Let n be the smallest positive integer for which 2^(8n) > x.
|
||||
n := (bits.Len64(x) + 7) / 8
|
||||
if n == 0 {
|
||||
n = 1
|
||||
}
|
||||
// Prepend number of encoded bytes
|
||||
b[i-1] = 9 - i
|
||||
return b[i-1:]
|
||||
// Return n || x with n as a byte and x an n bytes in big-endian order.
|
||||
b := make([]byte, 9)
|
||||
binary.BigEndian.PutUint64(b[1:], x)
|
||||
b = b[9-n-1:]
|
||||
b[0] = byte(n)
|
||||
return b
|
||||
}
|
||||
|
||||
func newCShake(N, S []byte, rate, outputLen int, dsbyte byte) ShakeHash {
|
||||
c := cshakeState{state: &state{rate: rate, outputLen: outputLen, dsbyte: dsbyte}}
|
||||
|
||||
// leftEncode returns max 9 bytes
|
||||
c.initBlock = make([]byte, 0, 9*2+len(N)+len(S))
|
||||
c.initBlock = append(c.initBlock, leftEncode(uint64(len(N)*8))...)
|
||||
c.initBlock = make([]byte, 0, 9+len(N)+9+len(S)) // leftEncode returns max 9 bytes
|
||||
c.initBlock = append(c.initBlock, leftEncode(uint64(len(N))*8)...)
|
||||
c.initBlock = append(c.initBlock, N...)
|
||||
c.initBlock = append(c.initBlock, leftEncode(uint64(len(S)*8))...)
|
||||
c.initBlock = append(c.initBlock, leftEncode(uint64(len(S))*8)...)
|
||||
c.initBlock = append(c.initBlock, S...)
|
||||
c.Write(bytepad(c.initBlock, c.rate))
|
||||
return &c
|
||||
@@ -111,24 +106,50 @@ func (c *state) Clone() ShakeHash {
|
||||
return c.clone()
|
||||
}
|
||||
|
||||
func (c *cshakeState) MarshalBinary() ([]byte, error) {
|
||||
return c.AppendBinary(make([]byte, 0, marshaledSize+len(c.initBlock)))
|
||||
}
|
||||
|
||||
func (c *cshakeState) AppendBinary(b []byte) ([]byte, error) {
|
||||
b, err := c.state.AppendBinary(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b = append(b, c.initBlock...)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (c *cshakeState) UnmarshalBinary(b []byte) error {
|
||||
if len(b) <= marshaledSize {
|
||||
return errors.New("sha3: invalid hash state")
|
||||
}
|
||||
if err := c.state.UnmarshalBinary(b[:marshaledSize]); err != nil {
|
||||
return err
|
||||
}
|
||||
c.initBlock = bytes.Clone(b[marshaledSize:])
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewShake128 creates a new SHAKE128 variable-output-length ShakeHash.
|
||||
// Its generic security strength is 128 bits against all attacks if at
|
||||
// least 32 bytes of its output are used.
|
||||
func NewShake128() ShakeHash {
|
||||
if h := newShake128Asm(); h != nil {
|
||||
return h
|
||||
}
|
||||
return &state{rate: rate128, outputLen: 32, dsbyte: dsbyteShake}
|
||||
return newShake128()
|
||||
}
|
||||
|
||||
// NewShake256 creates a new SHAKE256 variable-output-length ShakeHash.
|
||||
// Its generic security strength is 256 bits against all attacks if
|
||||
// at least 64 bytes of its output are used.
|
||||
func NewShake256() ShakeHash {
|
||||
if h := newShake256Asm(); h != nil {
|
||||
return h
|
||||
}
|
||||
return &state{rate: rate256, outputLen: 64, dsbyte: dsbyteShake}
|
||||
return newShake256()
|
||||
}
|
||||
|
||||
func newShake128Generic() *state {
|
||||
return &state{rate: rateK256, outputLen: 32, dsbyte: dsbyteShake}
|
||||
}
|
||||
|
||||
func newShake256Generic() *state {
|
||||
return &state{rate: rateK512, outputLen: 64, dsbyte: dsbyteShake}
|
||||
}
|
||||
|
||||
// NewCShake128 creates a new instance of cSHAKE128 variable-output-length ShakeHash,
|
||||
@@ -141,7 +162,7 @@ func NewCShake128(N, S []byte) ShakeHash {
|
||||
if len(N) == 0 && len(S) == 0 {
|
||||
return NewShake128()
|
||||
}
|
||||
return newCShake(N, S, rate128, 32, dsbyteCShake)
|
||||
return newCShake(N, S, rateK256, 32, dsbyteCShake)
|
||||
}
|
||||
|
||||
// NewCShake256 creates a new instance of cSHAKE256 variable-output-length ShakeHash,
|
||||
@@ -154,7 +175,7 @@ func NewCShake256(N, S []byte) ShakeHash {
|
||||
if len(N) == 0 && len(S) == 0 {
|
||||
return NewShake256()
|
||||
}
|
||||
return newCShake(N, S, rate256, 64, dsbyteCShake)
|
||||
return newCShake(N, S, rateK512, 64, dsbyteCShake)
|
||||
}
|
||||
|
||||
// ShakeSum128 writes an arbitrary-length digest of data into hash.
|
||||
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !gc || purego || !s390x
|
||||
|
||||
package sha3
|
||||
|
||||
// newShake128Asm returns an assembly implementation of SHAKE-128 if available,
|
||||
// otherwise it returns nil.
|
||||
func newShake128Asm() ShakeHash {
|
||||
return nil
|
||||
}
|
||||
|
||||
// newShake256Asm returns an assembly implementation of SHAKE-256 if available,
|
||||
// otherwise it returns nil.
|
||||
func newShake256Asm() ShakeHash {
|
||||
return nil
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !gc || purego || !s390x
|
||||
|
||||
package sha3
|
||||
|
||||
func newShake128() *state {
|
||||
return newShake128Generic()
|
||||
}
|
||||
|
||||
func newShake256() *state {
|
||||
return newShake256Generic()
|
||||
}
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (!amd64 && !386 && !ppc64le) || purego
|
||||
|
||||
package sha3
|
||||
|
||||
// A storageBuf is an aligned array of maxRate bytes.
|
||||
type storageBuf [maxRate]byte
|
||||
|
||||
func (b *storageBuf) asBytes() *[maxRate]byte {
|
||||
return (*[maxRate]byte)(b)
|
||||
}
|
||||
|
||||
var (
|
||||
xorIn = xorInGeneric
|
||||
copyOut = copyOutGeneric
|
||||
xorInUnaligned = xorInGeneric
|
||||
copyOutUnaligned = copyOutGeneric
|
||||
)
|
||||
|
||||
const xorImplementationUnaligned = "generic"
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sha3
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
// xorInGeneric xors the bytes in buf into the state; it
|
||||
// makes no non-portable assumptions about memory layout
|
||||
// or alignment.
|
||||
func xorInGeneric(d *state, buf []byte) {
|
||||
n := len(buf) / 8
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
a := binary.LittleEndian.Uint64(buf)
|
||||
d.a[i] ^= a
|
||||
buf = buf[8:]
|
||||
}
|
||||
}
|
||||
|
||||
// copyOutGeneric copies uint64s to a byte buffer.
|
||||
func copyOutGeneric(d *state, b []byte) {
|
||||
for i := 0; len(b) >= 8; i++ {
|
||||
binary.LittleEndian.PutUint64(b, d.a[i])
|
||||
b = b[8:]
|
||||
}
|
||||
}
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (amd64 || 386 || ppc64le) && !purego
|
||||
|
||||
package sha3
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// A storageBuf is an aligned array of maxRate bytes.
|
||||
type storageBuf [maxRate / 8]uint64
|
||||
|
||||
func (b *storageBuf) asBytes() *[maxRate]byte {
|
||||
return (*[maxRate]byte)(unsafe.Pointer(b))
|
||||
}
|
||||
|
||||
// xorInUnaligned uses unaligned reads and writes to update d.a to contain d.a
|
||||
// XOR buf.
|
||||
func xorInUnaligned(d *state, buf []byte) {
|
||||
n := len(buf)
|
||||
bw := (*[maxRate / 8]uint64)(unsafe.Pointer(&buf[0]))[: n/8 : n/8]
|
||||
if n >= 72 {
|
||||
d.a[0] ^= bw[0]
|
||||
d.a[1] ^= bw[1]
|
||||
d.a[2] ^= bw[2]
|
||||
d.a[3] ^= bw[3]
|
||||
d.a[4] ^= bw[4]
|
||||
d.a[5] ^= bw[5]
|
||||
d.a[6] ^= bw[6]
|
||||
d.a[7] ^= bw[7]
|
||||
d.a[8] ^= bw[8]
|
||||
}
|
||||
if n >= 104 {
|
||||
d.a[9] ^= bw[9]
|
||||
d.a[10] ^= bw[10]
|
||||
d.a[11] ^= bw[11]
|
||||
d.a[12] ^= bw[12]
|
||||
}
|
||||
if n >= 136 {
|
||||
d.a[13] ^= bw[13]
|
||||
d.a[14] ^= bw[14]
|
||||
d.a[15] ^= bw[15]
|
||||
d.a[16] ^= bw[16]
|
||||
}
|
||||
if n >= 144 {
|
||||
d.a[17] ^= bw[17]
|
||||
}
|
||||
if n >= 168 {
|
||||
d.a[18] ^= bw[18]
|
||||
d.a[19] ^= bw[19]
|
||||
d.a[20] ^= bw[20]
|
||||
}
|
||||
}
|
||||
|
||||
func copyOutUnaligned(d *state, buf []byte) {
|
||||
ab := (*[maxRate]uint8)(unsafe.Pointer(&d.a[0]))
|
||||
copy(buf, ab[:])
|
||||
}
|
||||
|
||||
var (
|
||||
xorIn = xorInUnaligned
|
||||
copyOut = copyOutUnaligned
|
||||
)
|
||||
|
||||
const xorImplementationUnaligned = "unaligned"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user