package bash import ( "fmt" "path" "strings" ) type fsNode struct { name string isDir bool content string children map[string]*fsNode } type filesystem struct { root *fsNode } func newFilesystem(hostname string) *filesystem { fs := &filesystem{ root: &fsNode{name: "/", isDir: true, children: make(map[string]*fsNode)}, } fs.mkdirAll("/etc") fs.mkdirAll("/root") fs.mkdirAll("/home") fs.mkdirAll("/var/log") fs.mkdirAll("/tmp") fs.mkdirAll("/usr/bin") fs.mkdirAll("/usr/local") fs.writeFile("/etc/passwd", "root:x:0:0:root:/root:/bin/bash\n"+ "daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin\n"+ "www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin\n"+ "mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/false\n") fs.writeFile("/etc/hostname", hostname+"\n") fs.writeFile("/etc/hosts", "127.0.0.1\tlocalhost\n"+ "127.0.1.1\t"+hostname+"\n"+ "::1\t\tlocalhost ip6-localhost ip6-loopback\n") fs.writeFile("/root/.bash_history", "apt update\n"+ "apt upgrade -y\n"+ "systemctl restart nginx\n"+ "tail -f /var/log/syslog\n"+ "df -h\n"+ "free -m\n"+ "netstat -tlnp\n"+ "cat /etc/passwd\n") fs.writeFile("/root/.bashrc", "# ~/.bashrc: executed by bash(1) for non-login shells.\n"+ "export PS1='\\u@\\h:\\w\\$ '\n"+ "alias ll='ls -alF'\n"+ "alias la='ls -A'\n") fs.writeFile("/root/README.txt", "Production server - DO NOT MODIFY\n") fs.writeFile("/var/log/syslog", "Jan 12 03:14:22 "+hostname+" systemd[1]: Started Daily apt download activities.\n"+ "Jan 12 03:14:23 "+hostname+" systemd[1]: Started Daily Cleanup of Temporary Directories.\n"+ "Jan 12 04:00:01 "+hostname+" CRON[12345]: (root) CMD (/usr/local/bin/backup.sh)\n"+ "Jan 12 04:00:03 "+hostname+" kernel: [UFW BLOCK] IN=eth0 OUT= SRC=203.0.113.42 DST=10.0.0.5 PROTO=TCP DPT=22\n") fs.writeFile("/tmp/notes.txt", "TODO: Update SSL certificates\n") return fs } // resolvePath converts a potentially relative path to an absolute one. func resolvePath(cwd, p string) string { if !strings.HasPrefix(p, "/") { p = cwd + "/" + p } return path.Clean(p) } func (fs *filesystem) lookup(p string) *fsNode { p = path.Clean(p) if p == "/" { return fs.root } parts := strings.Split(strings.TrimPrefix(p, "/"), "/") node := fs.root for _, part := range parts { if node.children == nil { return nil } child, ok := node.children[part] if !ok { return nil } node = child } return node } func (fs *filesystem) exists(p string) bool { return fs.lookup(p) != nil } func (fs *filesystem) isDirectory(p string) bool { n := fs.lookup(p) return n != nil && n.isDir } func (fs *filesystem) list(p string) ([]string, error) { n := fs.lookup(p) if n == nil { return nil, fmt.Errorf("ls: cannot access '%s': No such file or directory", p) } if !n.isDir { return nil, fmt.Errorf("ls: cannot access '%s': Not a directory", p) } names := make([]string, 0, len(n.children)) for name, child := range n.children { if child.isDir { name += "/" } names = append(names, name) } return names, nil } func (fs *filesystem) read(p string) (string, error) { n := fs.lookup(p) if n == nil { return "", fmt.Errorf("cat: %s: No such file or directory", p) } if n.isDir { return "", fmt.Errorf("cat: %s: Is a directory", p) } return n.content, nil } func (fs *filesystem) mkdirAll(p string) { p = path.Clean(p) parts := strings.Split(strings.TrimPrefix(p, "/"), "/") node := fs.root for _, part := range parts { if node.children == nil { node.children = make(map[string]*fsNode) } child, ok := node.children[part] if !ok { child = &fsNode{name: part, isDir: true, children: make(map[string]*fsNode)} node.children[part] = child } node = child } } func (fs *filesystem) writeFile(p string, content string) { p = path.Clean(p) dir := path.Dir(p) base := path.Base(p) fs.mkdirAll(dir) parent := fs.lookup(dir) parent.children[base] = &fsNode{name: base, content: content} }