Commit 44d7a9b8 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Copy darwin mount code from Russ Cox' FUSE.

Split off some linux specific tests.
parent a0003691
......@@ -117,24 +117,6 @@ func TestOpenUnreadable(t *testing.T) {
}
}
func TestTouch(t *testing.T) {
ts := NewTestCase(t)
defer ts.Cleanup()
contents := []byte{1, 2, 3}
err := ioutil.WriteFile(ts.origFile, []byte(contents), 0700)
CheckSuccess(err)
err = os.Chtimes(ts.mountFile, time.Unix(42, 0), time.Unix(43, 0))
CheckSuccess(err)
var stat syscall.Stat_t
err = syscall.Lstat(ts.mountFile, &stat)
CheckSuccess(err)
if stat.Atim.Sec != 42 || stat.Mtim.Sec != 43 {
t.Errorf("Got wrong timestamps %v", stat)
}
}
func TestReadThrough(t *testing.T) {
ts := NewTestCase(t)
defer ts.Cleanup()
......@@ -710,15 +692,6 @@ func TestIoctl(t *testing.T) {
ioctl(int(f.Fd()), 0x5401, 42)
}
func clearStatfs(s *syscall.Statfs_t) {
empty := syscall.Statfs_t{}
s.Type = 0
s.Fsid = empty.Fsid
s.Spare = empty.Spare
// TODO - figure out what this is for.
s.Flags = 0
}
// This test is racy. If an external process consumes space while this
// runs, we may see spurious differences between the two statfs() calls.
func TestStatFs(t *testing.T) {
......
package fuse
import (
"io/ioutil"
"os"
"syscall"
"testing"
"time"
)
func TestTouch(t *testing.T) {
ts := NewTestCase(t)
defer ts.Cleanup()
contents := []byte{1, 2, 3}
err := ioutil.WriteFile(ts.origFile, []byte(contents), 0700)
CheckSuccess(err)
err = os.Chtimes(ts.mountFile, time.Unix(42, 0), time.Unix(43, 0))
CheckSuccess(err)
var stat syscall.Stat_t
err = syscall.Lstat(ts.mountFile, &stat)
CheckSuccess(err)
if stat.Atim.Sec != 42 || stat.Mtim.Sec != 43 {
t.Errorf("Got wrong timestamps %v", stat)
}
}
func clearStatfs(s *syscall.Statfs_t) {
empty := syscall.Statfs_t{}
s.Type = 0
s.Fsid = empty.Fsid
s.Spare = empty.Spare
// TODO - figure out what this is for.
s.Flags = 0
}
package fuse
import (
"io/ioutil"
"os"
"syscall"
"testing"
......@@ -25,27 +24,3 @@ func TestToStatus(t *testing.T) {
t.Errorf("Wrong conversion %v != %v", errNo, syscall.ENOENT)
}
}
func TestLinkAt(t *testing.T) {
dir, _ := ioutil.TempDir("", "go-fuse")
ioutil.WriteFile(dir+"/a", []byte{42}, 0644)
f, _ := os.Open(dir)
e := Linkat(int(f.Fd()), "a", int(f.Fd()), "b")
if e != 0 {
t.Fatalf("Linkat %d", e)
}
var s1, s2 syscall.Stat_t
err := syscall.Lstat(dir+"/a", &s1)
if err != nil {
t.Fatalf("Lstat a: %v", err)
}
err = syscall.Lstat(dir+"/b", &s2)
if err != nil {
t.Fatalf("Lstat b: %v", err)
}
if s1.Ino != s2.Ino {
t.Fatal("Ino mismatch", s1, s2)
}
}
package fuse
import (
"io/ioutil"
"os"
"syscall"
"testing"
)
func TestLinkAt(t *testing.T) {
dir, _ := ioutil.TempDir("", "go-fuse")
ioutil.WriteFile(dir+"/a", []byte{42}, 0644)
f, _ := os.Open(dir)
e := Linkat(int(f.Fd()), "a", int(f.Fd()), "b")
if e != 0 {
t.Fatalf("Linkat %d", e)
}
var s1, s2 syscall.Stat_t
err := syscall.Lstat(dir+"/a", &s1)
if err != nil {
t.Fatalf("Lstat a: %v", err)
}
err = syscall.Lstat(dir+"/b", &s2)
if err != nil {
t.Fatalf("Lstat b: %v", err)
}
if s1.Ino != s2.Ino {
t.Fatal("Ino mismatch", s1, s2)
}
}
// Copyright 2011 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.
// TODO: Rewrite using package syscall not cgo
package fuse
/*
// Adapted from Plan 9 from User Space's src/cmd/9pfuse/fuse.c,
// which carries this notice:
//
// The files in this directory are subject to the following license.
//
// The author of this software is Russ Cox.
//
// Copyright (c) 2006 Russ Cox
//
// Permission to use, copy, modify, and distribute this software for any
// purpose without fee is hereby granted, provided that this entire notice
// is included in all copies of any software which is or includes a copy
// or modification of this software and in all copies of the supporting
// documentation for such software.
//
// THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
// WARRANTY. IN PARTICULAR, THE AUTHOR MAKES NO REPRESENTATION OR WARRANTY
// OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS
// FITNESS FOR ANY PARTICULAR PURPOSE.
#include <stdlib.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#define nil ((void*)0)
static int
mountfuse(char *mtpt, char **err)
{
int i, pid, fd, r;
char buf[200];
struct vfsconf vfs;
char *f;
if(getvfsbyname("fusefs", &vfs) < 0){
if(access(f="/Library/Filesystems/osxfusefs.fs"
"/Support/load_osxfusefs", 0) < 0){
*err = strdup("cannot find load_fusefs");
return -1;
}
if((r=system(f)) < 0){
snprintf(buf, sizeof buf, "%s: %s", f, strerror(errno));
*err = strdup(buf);
return -1;
}
if(r != 0){
snprintf(buf, sizeof buf, "load_fusefs failed: exit %d", r);
*err = strdup(buf);
return -1;
}
if(getvfsbyname("osxfusefs", &vfs) < 0){
snprintf(buf, sizeof buf, "getvfsbyname osxfusefs: %s", strerror(errno));
*err = strdup(buf);
return -1;
}
}
// Look for available FUSE device.
for(i=0;; i++){
snprintf(buf, sizeof buf, "/dev/osxfuse%d", i);
if(access(buf, 0) < 0){
*err = strdup("no available fuse devices");
return -1;
}
if((fd = open(buf, O_RDWR)) >= 0)
break;
}
pid = fork();
if(pid < 0)
return -1;
if(pid == 0){
snprintf(buf, sizeof buf, "%d", fd);
setenv("MOUNT_FUSEFS_CALL_BY_LIB", "", 1);
// Different versions of MacFUSE put the
// mount_fusefs binary in different places.
// Try all.
// Leopard location
setenv("MOUNT_FUSEFS_DAEMON_PATH",
"/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs", 1);
execl("/Library/Filesystems/osxfusefs.fs/Support/mount_osxfusefs",
"mount_osxfusefs",
"-o", "iosize=4096", buf, mtpt, nil);
fprintf(stderr, "exec mount_osxfusefs: %s\n", strerror(errno));
_exit(1);
}
return fd;
}
*/
import "C"
import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"unsafe"
)
func mount(dir string, options string) (connection *os.File, err error) {
errp := (**C.char)(C.malloc(16))
*errp = nil
defer C.free(unsafe.Pointer(errp))
cdir := C.CString(dir)
defer C.free(unsafe.Pointer(cdir))
fd := C.mountfuse(cdir, errp)
if *errp != nil {
return nil, mountError(C.GoString(*errp))
}
return os.NewFile(uintptr(fd), "<fuseConnection>"), nil
}
type mountError string
func (m mountError) Error() string {
return string(m)
}
func unmount(mountPoint string) error {
dir, _ := filepath.Split(mountPoint)
proc, err := os.StartProcess(umountBinary,
[]string{umountBinary, mountPoint},
&os.ProcAttr{Dir: dir, Files: []*os.File{nil, nil, os.Stderr}})
if err != nil {
return err
}
w, err := proc.Wait()
if !w.Success() {
return fmt.Errorf("umount exited with code %v\n", w.Sys())
}
return err
}
var umountBinary string
func init() {
var err error
umountBinary, err = exec.LookPath("umount")
if err != nil {
log.Fatalf("Could not find umount binary: %v", err)
}
}
......@@ -26,7 +26,7 @@ func unixgramSocketpair() (l, r *os.File, err error) {
// Create a FUSE FS on the specified mount point. The returned
// mount point is always absolute.
func mount(mountPoint string, options string) (f *os.File, finalMountPoint string, err error) {
func mount(mountPoint string, options string) (f *os.File, err error) {
local, remote, err := unixgramSocketpair()
if err != nil {
return
......@@ -35,16 +35,6 @@ func mount(mountPoint string, options string) (f *os.File, finalMountPoint strin
defer local.Close()
defer remote.Close()
mountPoint = filepath.Clean(mountPoint)
if !filepath.IsAbs(mountPoint) {
cwd := ""
cwd, err = os.Getwd()
if err != nil {
return
}
mountPoint = filepath.Clean(filepath.Join(cwd, mountPoint))
}
cmd := []string{fusermountBinary, mountPoint}
if options != "" {
cmd = append(cmd, "-o")
......@@ -69,9 +59,7 @@ func mount(mountPoint string, options string) (f *os.File, finalMountPoint strin
return
}
f, err = getConnection(local)
finalMountPoint = mountPoint
return
return getConnection(local)
}
func privilegedUnmount(mountPoint string) error {
......
......@@ -4,6 +4,7 @@ import (
"fmt"
"log"
"os"
"path/filepath"
"strings"
"sync"
"time"
......@@ -116,7 +117,15 @@ func (ms *MountState) Mount(mountPoint string, opts *MountOptions) error {
}
optStrs = append(optStrs, "subtype="+name)
file, mp, err := mount(mountPoint, strings.Join(optStrs, ","))
mountPoint = filepath.Clean(mountPoint)
if !filepath.IsAbs(mountPoint) {
cwd, err := os.Getwd()
if err != nil {
return err
}
mountPoint = filepath.Clean(filepath.Join(cwd, mountPoint))
}
file, err := mount(mountPoint, strings.Join(optStrs, ","))
if err != nil {
return err
}
......@@ -132,7 +141,7 @@ func (ms *MountState) Mount(mountPoint string, opts *MountOptions) error {
},
}
ms.fileSystem.Init(&initParams)
ms.mountPoint = mp
ms.mountPoint = mountPoint
ms.mountFile = file
return nil
}
......
package raw
import (
"fmt"
"syscall"
)
func (a *Attr) String() string {
return fmt.Sprintf(
"{M0%o SZ=%d L=%d "+
"%d:%d "+
"%d %d:%d "+
"A %d.%09d "+
"M %d.%09d "+
"C %d.%09d}",
a.Mode, a.Size, a.Nlink,
a.Uid, a.Gid,
a.Blocks,
a.Rdev, a.Ino, a.Atime, a.Atimensec, a.Mtime, a.Mtimensec,
a.Ctime, a.Ctimensec)
}
package raw
import (
"fmt"
"syscall"
)
......@@ -8,3 +9,19 @@ func init() {
OpenFlagNames[syscall.O_LARGEFILE] = "LARGEFILE"
OpenFlagNames[syscall_O_NOATIME] = "NOATIME"
}
func (a *Attr) String() string {
return fmt.Sprintf(
"{M0%o SZ=%d L=%d "+
"%d:%d "+
"%d*%d %d:%d "+
"A %d.%09d "+
"M %d.%09d "+
"C %d.%09d}",
a.Mode, a.Size, a.Nlink,
a.Uid, a.Gid,
a.Blocks, a.Blksize,
a.Rdev, a.Ino, a.Atime, a.Atimensec, a.Mtime, a.Mtimensec,
a.Ctime, a.Ctimensec)
}
......@@ -55,7 +55,7 @@ const ( // SetAttrIn.Valid
FATTR_LOCKOWNER = (1 << 9)
)
type SetAttrIn struct {
type SetAttrInCommon struct {
Valid uint32
Padding uint32
Fh uint64
......@@ -322,24 +322,6 @@ type FlushIn struct {
LockOwner uint64
}
type Attr struct {
Ino uint64
Size uint64
Blocks uint64
Atime uint64
Mtime uint64
Ctime uint64
Atimensec uint32
Mtimensec uint32
Ctimensec uint32
Mode uint32
Nlink uint32
Owner
Rdev uint32
Blksize uint32
Padding uint32
}
type EntryOut struct {
NodeId uint64
Generation uint64
......
package raw
type Attr struct {
Ino uint64
Size uint64
Blocks uint64
Atime uint64
Mtime uint64
Ctime uint64
Crtime_ uint64 // OS X
Atimensec uint32
Mtimensec uint32
Ctimensec uint32
Crtimensec_ uint32 // OS X
Mode uint32
Nlink uint32
Owner
Rdev uint32
Flags_ uint32 // OS X
}
type SetAttrIn struct {
SetAttrInCommon
// OS X only
Bkuptime_ uint64
Chgtime_ uint64
Crtime uint64
BkuptimeNsec uint32
ChgtimeNsec uint32
CrtimeNsec uint32
Flags_ uint32 // see chflags(2)
}
package raw
type Attr struct {
Ino uint64
Size uint64
Blocks uint64
Atime uint64
Mtime uint64
Ctime uint64
Atimensec uint32
Mtimensec uint32
Ctimensec uint32
Mode uint32
Nlink uint32
Owner
Rdev uint32
Blksize uint32
Padding uint32
}
type SetAttrIn struct {
SetAttrInCommon
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment