Files
opensandbox-group--opensandbox/components/execd/pkg/web/controller/utils_windows.go
T
wehub-resource-sync e0e362d700
SDK Tests / changes (push) Successful in 2m29s
Real E2E Tests / changes (push) Successful in 2m29s
Deploy Docs Pages / build (push) Has been cancelled
Deploy Docs Pages / deploy (push) Has been cancelled
Real E2E Tests / JavaScript E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Python E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Java E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / C# E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Go E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Real E2E CI (push) Has been cancelled
SDK Tests / SDK CI (push) Has been cancelled
SDK Tests / CLI Tests (push) Has been cancelled
SDK Tests / Python SDK Quality (code-interpreter) (push) Has been cancelled
SDK Tests / Python SDK Quality (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (sandbox) (push) Has been cancelled
SDK Tests / CLI Quality (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Go SDK Quality And Tests (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:39:33 +08:00

318 lines
7.2 KiB
Go

// Copyright 2025 Alibaba Group Holding Ltd.
//
// 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 windows
// +build windows
package controller
import (
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"strconv"
"strings"
"syscall"
"time"
"github.com/alibaba/opensandbox/execd/pkg/util/pathutil"
"github.com/alibaba/opensandbox/execd/pkg/web/model"
)
func DeleteFile(filePath string) error {
absPath, err := pathutil.ExpandAbsPath(filePath)
if err != nil {
return fmt.Errorf("invalid path: %w", err)
}
fileInfo, err := os.Stat(absPath)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return nil
}
return err
}
if fileInfo.IsDir() {
return fmt.Errorf("path is a directory: %s", filePath)
}
if err := os.Remove(absPath); err != nil {
return fmt.Errorf("failed to remove file: %w", err)
}
return nil
}
func ChmodFile(file string, perms model.Permission) error {
abs, err := pathutil.ExpandAbsPath(file)
if err != nil {
return err
}
if perms.Mode != 0 {
mode, err := strconv.ParseUint(strconv.Itoa(perms.Mode), 8, 32)
if err != nil {
return err
}
err = os.Chmod(abs, os.FileMode(mode))
if err != nil {
return err
}
}
return SetFileOwnership(abs, perms.Owner, perms.Group)
}
// SetFileOwnership is a placeholder on Windows where POSIX ownership is not supported.
func SetFileOwnership(_ string, _ string, _ string) error {
// TODO: add Windows ACL support if needed.
return nil
}
func RenameFile(item model.RenameFileItem) error {
srcPath, err := pathutil.ExpandAbsPath(item.Src)
if err != nil {
return fmt.Errorf("invalid source path: %w", err)
}
dstPath, err := pathutil.ExpandAbsPath(item.Dest)
if err != nil {
return fmt.Errorf("invalid destination path: %w", err)
}
if _, err := os.Stat(srcPath); os.IsNotExist(err) {
return fmt.Errorf("source path not found: %s: %w", item.Src, err)
}
dstDir := filepath.Dir(dstPath)
if err := os.MkdirAll(dstDir, 0755); err != nil {
return fmt.Errorf("failed to create destination directory: %w", err)
}
if _, err := os.Stat(dstPath); err == nil {
return fmt.Errorf("destination path already exists: %s", item.Dest)
}
if err := os.Rename(srcPath, dstPath); err != nil {
return fmt.Errorf("failed to rename file: %w", err)
}
return nil
}
// MkdirAllWithOwnership creates targetDir and any missing parents, then applies
// owner/group only to the directories that were actually created (not pre-existing ones).
func MkdirAllWithOwnership(targetDir string, dirPerm os.FileMode, owner, group string) error {
firstNew := ""
cur := targetDir
for {
if _, err := os.Stat(cur); err == nil {
break
}
firstNew = cur
parent := filepath.Dir(cur)
if parent == cur {
break
}
cur = parent
}
if err := os.MkdirAll(targetDir, dirPerm); err != nil {
return err
}
if firstNew == "" || (owner == "" && group == "") {
return nil
}
rel, err := filepath.Rel(firstNew, targetDir)
if err != nil {
return err
}
parts := strings.Split(rel, string(filepath.Separator))
cur = firstNew
if err := SetFileOwnership(cur, owner, group); err != nil {
return err
}
for _, p := range parts {
if p == "." {
continue
}
cur = filepath.Join(cur, p)
if err := SetFileOwnership(cur, owner, group); err != nil {
return err
}
}
return nil
}
func MakeDir(dir string, perm model.Permission) error {
abs, err := pathutil.ExpandAbsPath(dir)
if err != nil {
return err
}
_, statErr := os.Stat(abs)
existed := statErr == nil
if err := MkdirAllWithOwnership(abs, os.ModePerm, perm.Owner, perm.Group); err != nil {
return err
}
if !existed && perm.Mode != 0 {
mode, err := strconv.ParseUint(strconv.Itoa(perm.Mode), 8, 32)
if err != nil {
return err
}
return os.Chmod(abs, os.FileMode(mode))
}
return nil
}
func fileType(fileInfo os.FileInfo) string {
mode := fileInfo.Mode()
if mode&os.ModeSymlink != 0 {
return "symlink"
}
if fileInfo.IsDir() {
return "directory"
}
if mode.IsRegular() {
return "file"
}
return "other"
}
func buildFileInfo(absPath string, fileInfo os.FileInfo) (model.FileInfo, error) {
createdAt := getFileCreateTime(fileInfo)
if data, ok := fileInfo.Sys().(*syscall.Win32FileAttributeData); ok && data != nil {
createdAt = time.Unix(0, data.CreationTime.Nanoseconds())
}
mode := strconv.FormatInt(int64(fileInfo.Mode().Perm()), 8)
return model.FileInfo{
Path: absPath,
Type: fileType(fileInfo),
Size: fileInfo.Size(),
ModifiedAt: fileInfo.ModTime(),
CreatedAt: createdAt,
Permission: model.Permission{
Owner: "",
Group: "",
Mode: func() int {
i, _ := strconv.Atoi(mode)
return i
}(),
},
}, nil
}
func GetFileInfo(filePath string) (model.FileInfo, error) {
absPath, err := pathutil.ExpandAbsPath(filePath)
if err != nil {
return model.FileInfo{}, fmt.Errorf("invalid path %s: %w", filePath, err)
}
fileInfo, err := os.Lstat(absPath)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return model.FileInfo{}, fmt.Errorf("file not found: %s: %w", filePath, err)
}
return model.FileInfo{}, fmt.Errorf("error accessing file %s: %w", filePath, err)
}
return buildFileInfo(absPath, fileInfo)
}
func SearchFileMetadata(metadata map[string]model.FileMetadata, filePath string) (string, model.FileMetadata, bool) {
base := filepath.Base(filePath)
for path, info := range metadata {
if filepath.Base(path) == base {
return path, info, true
}
}
return "", model.FileMetadata{}, false
}
type httpRange struct {
start, length int64
}
func ParseRange(s string, size int64) ([]httpRange, error) {
if !strings.HasPrefix(s, "bytes=") {
return nil, errors.New("invalid range")
}
ranges := strings.Split(s[6:], ",")
result := make([]httpRange, 0, len(ranges))
for _, ra := range ranges {
ra = strings.TrimSpace(ra)
if ra == "" {
continue
}
i := strings.Index(ra, "-")
if i < 0 {
return nil, errors.New("invalid range")
}
start, end := strings.TrimSpace(ra[:i]), strings.TrimSpace(ra[i+1:])
var r httpRange
if start == "" {
// suffix-length
n, err := strconv.ParseInt(end, 10, 64)
if err != nil || n < 0 {
return nil, errors.New("invalid range")
}
if n > size {
n = size
}
r.start = size - n
r.length = size - r.start
} else {
// start-end
i, err := strconv.ParseInt(start, 10, 64)
if err != nil || i < 0 {
return nil, errors.New("invalid range")
}
if end == "" {
// start-
r.start = i
r.length = size - i
} else {
// start-end
j, err := strconv.ParseInt(end, 10, 64)
if err != nil || j < i {
return nil, errors.New("invalid range")
}
r.start = i
r.length = j - i + 1
}
}
if r.start >= size {
continue
}
if r.start+r.length > size {
r.length = size - r.start
}
result = append(result, r)
}
return result, nil
}