Skip to content

Commit

Permalink
Merge pull request #25 from kacf/syscall
Browse files Browse the repository at this point in the history
Implement proper partition size detection.
  • Loading branch information
kacf committed Jan 22, 2016
2 parents 1085878 + 27efbdf commit 082438d
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 24 deletions.
48 changes: 48 additions & 0 deletions ioctl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2016 Mender Software AS
//
// 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 "os"
import "syscall"
import "unsafe"

// This is a bit weird, Syscall() says it accepts uintptr in the request field,
// but this in fact not true. By inspecting the calls with strace, it's clear
// that the pointer value is being passed as an int to ioctl(), which is just
// wrong. So write the ioctl request value (int) directly into the pointer value
// instead.
type ioctlRequestValue uintptr

// Returns size in first return. Second returns true if descriptor is not a
// block device. If it's true, then error != nil. Last return is error
// condition.
func getBlockDeviceSize(file *os.File) (uint64, bool, error) {
var fd uintptr = file.Fd()
ioctlRequest := BLKGETSIZE64
var blkSize uint64

_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, fd,
uintptr(unsafe.Pointer(ioctlRequest)),
uintptr(unsafe.Pointer(&blkSize)))

if errno == syscall.ENOTTY {
// This means the descriptor is not a block device.
// ENOTTY... weird, I know.
return 0, true, errno
} else if errno != 0 {
return 0, false, errno
}

return blkSize, false, nil
}
20 changes: 20 additions & 0 deletions ioctl_32_bit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2016 Mender Software AS
//
// 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.

// +build arm 386

package main

// Taken from <sys/mount.h>
const BLKGETSIZE64 ioctlRequestValue = 0x80041272
20 changes: 20 additions & 0 deletions ioctl_64_bit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2016 Mender Software AS
//
// 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.

// +build amd64

package main

// Taken from <sys/mount.h>
const BLKGETSIZE64 ioctlRequestValue = 0x80081272
47 changes: 29 additions & 18 deletions rootfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
package main

import "fmt"
import "os"
import "io"
import "os"

func doRootfs(imageFile string) error {
act, err := getInactivePartition()
inact, err := getInactivePartition()
if err != nil {
return fmt.Errorf("Not able to determine inactive partition: "+
"%s\n", err.Error())
Expand All @@ -31,10 +31,10 @@ func doRootfs(imageFile string) error {
}
defer image_fd.Close()

part_fd, err := os.OpenFile(act, os.O_WRONLY, 0)
part_fd, err := os.OpenFile(inact, os.O_WRONLY, 0)
if err != nil {
return fmt.Errorf("Not able to open partition: %s: %s\n",
act, err.Error())
inact, err.Error())
}
defer part_fd.Close()

Expand All @@ -45,18 +45,28 @@ func doRootfs(imageFile string) error {
return fmt.Errorf("Unable to stat() file: %s: %s\n",
imageFile, err.Error())
}
part_info, err := part_fd.Stat()
if err != nil {
return fmt.Errorf("Unable to stat() partition: %s: %s\n",
act, err.Error())

var partSizeU uint64
var partSize int64

partSizeU, notBlockDevice, err := getBlockDeviceSize(part_fd)
if notBlockDevice {
part_info, err := part_fd.Stat()
if err != nil {
return fmt.Errorf("Unable to stat() partition: "+
"%s: %s\n", inact, err.Error())
}
partSize = part_info.Size()
} else if err != nil {
return fmt.Errorf("Unable to determine size of partition "+
"%s: %s", inact, err.Error())
} else {
partSize = int64(partSizeU)
}
if part_info.Size() < image_info.Size() {
// TODO!! Fix this to use syscall. The file size will always
// be small (block device)
// For now we need to ignore the error, since you cannot update
// an actual partition if this returns early.
//return fmt.Errorf("Partition is smaller than the given image " +
// "file. Aborting.\n")

if partSize < image_info.Size() {
return fmt.Errorf("Partition is smaller than the given image " +
"file.")
}

// Write image file into partition.
Expand All @@ -73,7 +83,8 @@ func doRootfs(imageFile string) error {
_, write_err := part_fd.Write(buf[0:read])
if write_err != nil {
return fmt.Errorf("Error while writing to "+
"partition: %s: %s\n", act, write_err.Error())
"partition: %s: %s\n",
inact, write_err.Error())
}
}

Expand All @@ -94,7 +105,7 @@ func doRootfs(imageFile string) error {
}

func enableUpdatedPartition() error {
act, err := getInactivePartitionNumber()
inact, err := getInactivePartitionNumber()
if err != nil {
return err
}
Expand All @@ -103,7 +114,7 @@ func enableUpdatedPartition() error {
if err != nil {
return err
}
err = setBootEnv("boot_part", act)
err = setBootEnv("boot_part", inact)
if err != nil {
return err
}
Expand Down
54 changes: 48 additions & 6 deletions rootfs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,50 @@ func TestMockRootfs(t *testing.T) {

// ---------------------------------------------------------------------

// Try to execute rootfs operation with the dummy file.
{
newRunner := &testRunnerMulti{}
newRunner.cmdlines = StringPointerList(
"mount ",
"fw_printenv boot_part",
"mount ",
"fw_printenv boot_part",
"fw_setenv upgrade_available 1",
"fw_setenv boot_part 3",
"fw_setenv bootcount 0")

mount_output :=
base_mount_device + "2 on / type ext4 (rw)\n" +
"proc on /proc type proc (rw,noexec,nosuid,nodev)\n" +
base_mount_device + "1 on /boot type ext4 (rw)\n"
newRunner.outputs = []string{
mount_output,
"boot_part=2",
mount_output,
"boot_part=2",
"",
"",
""}

newRunner.ret_codes = []int{
0,
0,
0,
0,
1,
0,
0}

runner = newRunner
if err := doMain([]string{"-rootfs", dummy}); err == nil {
t.Fatal("Enabling the partition should have failed")
} else {
assertErrorSubstring(t, err, "Unable to activate partition")
}
}

// ---------------------------------------------------------------------

// Now try to shrink one partition, it should now fail.

file := base_mount_device + "3"
Expand Down Expand Up @@ -157,14 +201,12 @@ func TestMockRootfs(t *testing.T) {
0}

runner = newRunner
//prev := getModTime(t, base_mount_device+"3")
prev := getModTime(t, base_mount_device+"3")
if err := doMain([]string{"-rootfs", dummy}); err == nil {
// Need to skip this for now. See comment about syscall
// in rootfs.go.
//t.Fatal("Updating image should have failed " +
// "(partition too small)")
t.Fatal("Updating image should have failed " +
"(partition too small)")
}
//assertTrue(t, prev == getModTime(t, base_mount_device+"3"))
assertTrue(t, prev == getModTime(t, base_mount_device+"3"))
}

// ---------------------------------------------------------------------
Expand Down

0 comments on commit 082438d

Please sign in to comment.