Security: Mitigate possible privilege escalation via SG_IO ioctl (CVE-2011-4127,...
[libguestfs.git] / resize / resize.ml
index 8e7057c..b4af809 100644 (file)
@@ -57,6 +57,7 @@ let infile, outfile, copy_boot_loader, debug, deletes, dryrun,
   let format = ref "" in
   let ignores = ref [] in
   let lv_expands = ref [] in
+  let machine_readable = ref false in
   let ntfsresize_force = ref false in
   let output_format = ref "" in
   let quiet = ref false in
@@ -83,6 +84,7 @@ let infile, outfile, copy_boot_loader, debug, deletes, dryrun,
     "--LV-expand", Arg.String (add lv_expands), "lv -\"-";
     "--lvexpand", Arg.String (add lv_expands), "lv -\"-";
     "--LVexpand", Arg.String (add lv_expands), "lv -\"-";
+    "--machine-readable", Arg.Set machine_readable, " Make output machine readable";
     "-n",        Arg.Set dryrun,            " Don't perform changes";
     "--dryrun",  Arg.Set dryrun,            " -\"-";
     "--dry-run", Arg.Set dryrun,            " -\"-";
@@ -125,6 +127,7 @@ read the man page virt-resize(1).
   let format = match !format with "" -> None | str -> Some str in
   let ignores = List.rev !ignores in
   let lv_expands = List.rev !lv_expands in
+  let machine_readable = !machine_readable in
   let ntfsresize_force = !ntfsresize_force in
   let output_format = match !output_format with "" -> None | str -> Some str in
   let quiet = !quiet in
@@ -132,6 +135,25 @@ read the man page virt-resize(1).
   let resizes_force = List.rev !resizes_force in
   let shrink = match !shrink with "" -> None | str -> Some str in
 
+  (* No arguments and machine-readable mode?  Print out some facts
+   * about what this binary supports.  We only need to print out new
+   * things added since this option, or things which depend on features
+   * of the appliance.
+   *)
+  if !disks = [] && machine_readable then (
+    printf "virt-resize\n";
+    printf "ntfsresize-force\n";
+    printf "32bitok\n";
+    let g = new G.guestfs () in
+    g#add_drive_opts "/dev/null";
+    g#launch ();
+    if feature_available g [| "ntfsprogs"; "ntfs3g" |] then
+      printf "ntfs\n";
+    if feature_available g [| "btrfs" |] then
+      printf "btrfs\n";
+    exit 0
+  );
+
   (* Verify we got exactly 2 disks. *)
   let infile, outfile =
     match List.rev !disks with
@@ -144,8 +166,9 @@ read the man page virt-resize(1).
   lv_expands, ntfsresize_force, output_format,
   quiet, resizes, resizes_force, shrink
 
-(* Default to true, since NTFS support is usually available. *)
+(* Default to true, since NTFS and btrfs support are usually available. *)
 let ntfs_available = ref true
+let btrfs_available = ref true
 
 (* Add in and out disks to the handle and launch. *)
 let connect_both_disks () =
@@ -163,6 +186,7 @@ let connect_both_disks () =
 
   (* Update features available in the daemon. *)
   ntfs_available := feature_available g [|"ntfsprogs"; "ntfs3g"|];
+  btrfs_available := feature_available g [|"btrfs"|];
 
   g
 
@@ -215,7 +239,6 @@ let () =
 (* Build a data structure describing the source disk's partition layout. *)
 type partition = {
   p_name : string;               (* Device name, like /dev/sda1. *)
-  p_size : int64;                (* Current size of this partition. *)
   p_part : G.partition;          (* Partition data from libguestfs. *)
   p_bootable : bool;             (* Is it bootable? *)
   p_mbr_id : int option;         (* MBR ID, if it has one. *)
@@ -296,7 +319,7 @@ let partitions : partition list =
           with G.Error _ -> None in
         let typ = get_partition_content name in
 
-        { p_name = name; p_size = part.G.part_size; p_part = part;
+        { p_name = name; p_part = part;
           p_bootable = bootable; p_mbr_id = mbr_id; p_type = typ;
           p_operation = OpCopy; p_target_partnum = 0 }
     ) parts in
@@ -312,11 +335,13 @@ let partitions : partition list =
    *)
   List.iter (
     function
-    | { p_name = name; p_size = size; p_type = ContentPV pv_size }
+    | { p_name = name; p_part = { G.part_size = size };
+        p_type = ContentPV pv_size }
         when size < pv_size ->
         error "%s: partition size %Ld < physical volume size %Ld"
           name size pv_size
-    | { p_name = name; p_size = size; p_type = ContentFS (_, fs_size) }
+    | { p_name = name; p_part = { G.part_size = size };
+        p_type = ContentFS (_, fs_size) }
         when size < fs_size ->
         error "%s: partition size %Ld < filesystem size %Ld"
           name size fs_size
@@ -373,12 +398,14 @@ let lvs =
 (* These functions tell us if we know how to expand the content of
  * a particular partition or LV, and what method to use.
  *)
-type expand_content_method = PVResize | Resize2fs | NTFSResize
+type expand_content_method =
+  | PVResize | Resize2fs | NTFSResize | BtrfsFilesystemResize
 
 let string_of_expand_content_method = function
   | PVResize -> "pvresize"
   | Resize2fs -> "resize2fs"
   | NTFSResize -> "ntfsresize"
+  | BtrfsFilesystemResize -> "btrfs-filesystem-resize"
 
 let can_expand_content =
   if expand_content then
@@ -387,6 +414,7 @@ let can_expand_content =
     | ContentPV _ -> true
     | ContentFS (("ext2"|"ext3"|"ext4"), _) -> true
     | ContentFS (("ntfs"), _) when !ntfs_available -> true
+    | ContentFS (("btrfs"), _) when !btrfs_available -> true
     | ContentFS (_, _) -> false
   else
     fun _ -> false
@@ -398,6 +426,7 @@ let expand_content_method =
     | ContentPV _ -> PVResize
     | ContentFS (("ext2"|"ext3"|"ext4"), _) -> Resize2fs
     | ContentFS (("ntfs"), _) when !ntfs_available -> NTFSResize
+    | ContentFS (("btrfs"), _) when !btrfs_available -> BtrfsFilesystemResize
     | ContentFS (_, _) -> assert false
   else
     fun _ -> assert false
@@ -457,7 +486,7 @@ let () =
  *)
 let mark_partition_for_resize ~option ?(force = false) p newsize =
   let name = p.p_name in
-  let oldsize = p.p_size in
+  let oldsize = p.p_part.G.part_size in
 
   (match p.p_operation with
    | OpResize _ ->
@@ -511,7 +540,7 @@ let () =
     let p = find_partition ~option dev in
 
     (* Parse the size field. *)
-    let oldsize = p.p_size in
+    let oldsize = p.p_part.G.part_size in
     let newsize = parse_size oldsize sizefield in
 
     if newsize <= 0L then
@@ -543,7 +572,7 @@ let calculate_surplus () =
     fun total p ->
       let newsize =
         match p.p_operation with
-        | OpCopy | OpIgnore -> p.p_size
+        | OpCopy | OpIgnore -> p.p_part.G.part_size
         | OpDelete -> 0L
         | OpResize newsize -> newsize in
       total +^ newsize
@@ -571,7 +600,7 @@ let () =
 
          let option = "--expand" in
          let p = find_partition ~option dev in
-         let oldsize = p.p_size in
+         let oldsize = p.p_part.G.part_size in
          mark_partition_for_resize ~option p (oldsize +^ surplus)
     );
     (match shrink with
@@ -582,7 +611,7 @@ let () =
 
          let option = "--shrink" in
          let p = find_partition ~option dev in
-         let oldsize = p.p_size in
+         let oldsize = p.p_part.G.part_size in
          mark_partition_for_resize ~option p (oldsize +^ surplus)
     )
   )
@@ -625,7 +654,7 @@ let () =
     printf "Summary of changes:\n\n";
 
     List.iter (
-      fun ({ p_name = name; p_size = oldsize } as p) ->
+      fun ({ p_name = name; p_part = { G.part_size = oldsize }} as p) ->
         let text =
           match p.p_operation with
           | OpCopy ->
@@ -785,7 +814,7 @@ let () =
           | OpDelete -> None            (* do nothing *)
           | OpIgnore | OpCopy ->        (* new partition, same size *)
               (* Size in sectors. *)
-              let size = (p.p_size +^ sectsize -^ 1L) /^ sectsize in
+              let size = (p.p_part.G.part_size +^ sectsize -^ 1L) /^ sectsize in
               Some (add_partition size)
           | OpResize newsize ->         (* new partition, resized *)
               (* Size in sectors. *)
@@ -855,7 +884,7 @@ let () =
     | ({ p_name = source; p_target_partnum = target_partnum;
          p_operation = (OpCopy | OpResize _) } as p) :: ps
         when target_partnum > 0 ->
-        let oldsize = p.p_size in
+        let oldsize = p.p_part.G.part_size in
         let newsize =
           match p.p_operation with OpResize s -> s | _ -> oldsize in
 
@@ -920,6 +949,14 @@ let () =
           g#e2fsck_f target;
           g#resize2fs target
       | NTFSResize -> g#ntfsresize_opts ~force:ntfsresize_force target
+      | BtrfsFilesystemResize ->
+          (* Complicated ...  Btrfs forces us to mount the filesystem
+           * in order to resize it.
+           *)
+          assert (Array.length (g#mounts ()) = 0);
+          g#mount_options "" target "/";
+          g#btrfs_filesystem_resize "/";
+          g#umount "/"
     in
 
     (* Expand partition content as required. *)