fsutils.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package fsutils
  15. import (
  16. "bufio"
  17. "bytes"
  18. "fmt"
  19. "os"
  20. "regexp"
  21. "strconv"
  22. "strings"
  23. "yunion.io/x/log"
  24. "yunion.io/x/pkg/errors"
  25. "yunion.io/x/pkg/utils"
  26. "yunion.io/x/onecloud/pkg/hostman/diskutils/deploy_iface"
  27. "yunion.io/x/onecloud/pkg/hostman/diskutils/fsutils/driver"
  28. "yunion.io/x/onecloud/pkg/hostman/guestfs"
  29. "yunion.io/x/onecloud/pkg/hostman/guestfs/fsdriver"
  30. "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
  31. "yunion.io/x/onecloud/pkg/util/fileutils2"
  32. "yunion.io/x/onecloud/pkg/util/procutils"
  33. "yunion.io/x/onecloud/pkg/util/regutils2"
  34. "yunion.io/x/onecloud/pkg/util/xfsutils"
  35. )
  36. var ext4UsageTypeLargefileSize int64 = 1024 * 1024 * 1024 * 1024 * 4
  37. var ext4UsageTypeHugefileSize int64 = 1024 * 1024 * 1024 * 512
  38. func SetExt4UsageTypeThresholds(largefile, hugefile int64) {
  39. if largefile > 0 {
  40. ext4UsageTypeLargefileSize = largefile
  41. }
  42. if hugefile > 0 {
  43. ext4UsageTypeHugefileSize = hugefile
  44. }
  45. }
  46. func IsPartedFsString(fsstr string) bool {
  47. return utils.IsInStringArray(strings.ToLower(fsstr), []string{
  48. "ext2", "ext3", "ext4", "xfs",
  49. "fat16", "fat32",
  50. "hfs", "hfs+", "hfsx",
  51. "linux-swap", "linux-swap(v1)",
  52. "ntfs", "reiserfs", "ufs", "btrfs",
  53. })
  54. }
  55. func ParseDiskPartition(dev string, lines []string) ([][]string, string) {
  56. var (
  57. parts = [][]string{}
  58. label string
  59. labelPartten = regexp.MustCompile(`Partition Table:\s+(?P<label>\w+)`)
  60. partten = regexp.MustCompile(`(?P<idx>\d+)\s+(?P<start>\d+)s\s+(?P<end>\d+)s\s+(?P<count>\d+)s`)
  61. )
  62. for _, line := range lines {
  63. if len(label) == 0 {
  64. m := regutils2.GetParams(labelPartten, line)
  65. if len(m) > 0 {
  66. label = m["label"]
  67. }
  68. }
  69. m := regutils2.GetParams(partten, line)
  70. if len(m) > 0 {
  71. var (
  72. idx = m["idx"]
  73. start = m["start"]
  74. end = m["end"]
  75. count = m["count"]
  76. devname = dev
  77. )
  78. if '0' <= dev[len(dev)-1] && dev[len(dev)-1] <= '9' {
  79. devname += "p"
  80. }
  81. devname += idx
  82. data := regexp.MustCompile(`\s+`).Split(strings.TrimSpace(line), -1)
  83. var disktype, fs, flag string
  84. var offset = 0
  85. if len(data) > 4 {
  86. if label == "msdos" {
  87. disktype = data[4]
  88. if len(data) > 5 && IsPartedFsString(data[5]) {
  89. fs = data[5]
  90. offset += 1
  91. }
  92. if len(data) > 5+offset {
  93. flag = data[5+offset]
  94. }
  95. } else if label == "gpt" {
  96. if IsPartedFsString(data[4]) {
  97. fs = data[4]
  98. offset += 1
  99. }
  100. if len(data) > 4+offset {
  101. disktype = data[4+offset]
  102. }
  103. if len(data) > 4+offset+1 {
  104. flag = data[4+offset+1]
  105. }
  106. }
  107. }
  108. var bootable = ""
  109. if len(flag) > 0 && strings.Index(flag, "boot") >= 0 {
  110. bootable = "true"
  111. }
  112. parts = append(parts, []string{idx, bootable, start, end, count, disktype, fs,
  113. devname})
  114. }
  115. }
  116. return parts, label
  117. }
  118. // use proc driver do resizefs
  119. func ResizeDiskFs(diskPath string, sizeMb int, mounted bool) error {
  120. fsutilDriver := NewFsutilDriver(driver.NewProcDriver())
  121. return fsutilDriver.ResizeDiskFs(diskPath, sizeMb, mounted)
  122. }
  123. func (d *SFsutilDriver) ResizeDiskFs(diskPath string, sizeMb int, mounted bool) error {
  124. fPath, fs, err := d.ResizeDiskPartition(diskPath, sizeMb)
  125. if err != nil {
  126. return err
  127. }
  128. err, _ = d.ResizePartitionFs(fPath, fs, false, mounted)
  129. if err != nil {
  130. return errors.Wrapf(err, "resize fs %s", fs)
  131. }
  132. return nil
  133. }
  134. func (d *SFsutilDriver) ResizeDiskPartition(diskPath string, sizeMb int) (string, string, error) {
  135. var cmds = []string{"parted", "-a", "none", "-s", diskPath, "--", "unit", "s", "print"}
  136. lines, err := d.Exec(cmds[0], cmds[1:]...)
  137. if err != nil {
  138. hasPartTable := func() bool {
  139. for _, line := range strings.Split(string(lines), "\n") {
  140. if strings.Contains(line, "Partition Table") {
  141. return true
  142. }
  143. }
  144. return false
  145. }
  146. if hasPartTable() {
  147. return "", "", nil
  148. }
  149. log.Errorf("resize disk fs fail, output: %s , error: %s", lines, err)
  150. return "", "", err
  151. }
  152. parts, label := ParseDiskPartition(diskPath, strings.Split(string(lines), "\n"))
  153. log.Infof("Parts: %v label: %s", parts, label)
  154. if label == "gpt" {
  155. retCode, stdout, stderr, e := d.ExecInputWait("gdisk", []string{diskPath}, []string{"r", "e", "Y", "w", "Y", "Y"})
  156. if e != nil {
  157. return "", "", errors.Wrap(e, "gdisk exec failed")
  158. }
  159. log.Infof("gdisk: %s %s", stdout, stderr)
  160. if retCode != 1 && retCode != 0 {
  161. return "", "", errors.Errorf("Exit Code %d: %s\n%s", retCode, stdout, stderr)
  162. }
  163. }
  164. if len(parts) > 0 && (label == "gpt" ||
  165. (label == "msdos" && parts[len(parts)-1][5] == "primary")) {
  166. var part = parts[len(parts)-1]
  167. fsType := part[6]
  168. if len(fsType) == 0 {
  169. partDev := fmt.Sprintf("%s%s", diskPath, part[0])
  170. fsType = d.GetFsFormat(partDev)
  171. log.Infof("blkid get fstype %s", fsType)
  172. }
  173. if part[5] == "lvm" || IsSupportResizeFs(fsType) || part[5] == "primary" || fsType == "LVM2_member" {
  174. // growpart script replace parted resizepart
  175. output, err := d.Exec("growpart", diskPath, part[0])
  176. if err != nil {
  177. return "", "", errors.Wrapf(err, "growpart failed %s", output)
  178. }
  179. return part[7], fsType, nil
  180. }
  181. }
  182. return "", "", nil
  183. }
  184. func (d *SFsutilDriver) ResizeDiskWithDiskId(diskId string, rootPartDev string, onlineResize bool) error {
  185. // find partition need resize
  186. resizeDev, err := d.GetResizeDevBySerial(diskId)
  187. if err != nil {
  188. return err
  189. }
  190. if resizeDev == "" {
  191. log.Errorf("failed find disk serial %s", diskId)
  192. return nil
  193. }
  194. partDev, fsType, err := d.ResizeDiskPartition(resizeDev, 0)
  195. if err != nil {
  196. return err
  197. }
  198. if partDev == "" || fsType == "" || fsType == "LVM2_member" {
  199. if (fsType == "" || fsType == "LVM2_member") && partDev != "" {
  200. // fsType empty and partDev not empty is lvm device
  201. resizeDev = partDev
  202. }
  203. if !d.IsLvmPvDevice(resizeDev) {
  204. fsType = d.GetFsFormat(resizeDev)
  205. err, _ := d.ResizePartitionFs(resizeDev, fsType, false, onlineResize)
  206. return err
  207. }
  208. if err := d.Pvresize(resizeDev); err != nil {
  209. return err
  210. }
  211. vg := d.GetVgOfPvDevice(resizeDev)
  212. if vg == "" {
  213. return nil
  214. }
  215. lvs, err := d.GetVgLvs(vg)
  216. if err != nil {
  217. log.Errorf("failed get vg lvs %s: %s", vg, err)
  218. }
  219. if len(lvs) == 0 {
  220. log.Infof("disk %s has no lv, skip resize", diskId)
  221. return nil
  222. }
  223. var resizeLv string
  224. if rootPartDev != "" {
  225. for i := range lvs {
  226. if lvs[i].LvPath == rootPartDev {
  227. resizeLv = rootPartDev
  228. break
  229. }
  230. }
  231. }
  232. if resizeLv == "" {
  233. if len(lvs) != 1 {
  234. log.Errorf("disk %s has multi lv and no rootfs, skip resize partition", diskId)
  235. return nil
  236. } else {
  237. resizeLv = lvs[0].LvPath
  238. }
  239. }
  240. err = d.ExtendLv(resizeLv)
  241. if err != nil {
  242. return err
  243. }
  244. fsType = d.GetFsFormat(resizeLv)
  245. err, _ = d.ResizePartitionFs(resizeLv, fsType, false, onlineResize)
  246. return err
  247. } else {
  248. err, _ = d.ResizePartitionFs(partDev, fsType, false, onlineResize)
  249. return err
  250. }
  251. }
  252. func IsSupportResizeFs(fs string) bool {
  253. if strings.HasPrefix(fs, "linux-swap") {
  254. return true
  255. } else if strings.HasPrefix(fs, "ext") {
  256. return true
  257. } else if fs == "xfs" || fs == "f2fs" {
  258. return true
  259. }
  260. return false
  261. }
  262. func (d *SFsutilDriver) ResizePartitionFs(fpath, fs string, raiseError, onlineResize bool) (error, bool) {
  263. log.Errorf("ResizePartitionFs fstype %s", fs)
  264. if len(fs) == 0 {
  265. return nil, false
  266. }
  267. var (
  268. cmds = [][]string{}
  269. uuids, _ = d.GetDevUuid(fpath)
  270. )
  271. if strings.HasPrefix(fs, "linux-swap") {
  272. if v, ok := uuids["UUID"]; ok {
  273. cmds = [][]string{{"mkswap", "-U", v, fpath}}
  274. } else {
  275. cmds = [][]string{{"mkswap", fpath}}
  276. }
  277. } else if strings.HasPrefix(fs, "ext") {
  278. if !onlineResize {
  279. if !d.FsckExtFs(fpath) {
  280. if raiseError {
  281. return fmt.Errorf("Failed to fsck ext fs %s", fpath), false
  282. } else {
  283. return nil, false
  284. }
  285. }
  286. }
  287. cmds = [][]string{{"resize2fs", fpath}}
  288. } else if fs == "xfs" {
  289. uuid := uuids["UUID"]
  290. if len(uuid) > 0 {
  291. xfsutils.LockXfsPartition(uuid)
  292. defer xfsutils.UnlockXfsPartition(uuid)
  293. }
  294. if !onlineResize {
  295. var tmpPoint = fmt.Sprintf("/tmp/%s", strings.Replace(fpath, "/", "_", -1))
  296. if _, err := d.Exec("mountpoint", tmpPoint); err == nil {
  297. output, err := d.Exec("umount", "-f", tmpPoint)
  298. if err != nil {
  299. log.Errorf("failed umount %s: %s, %s", tmpPoint, err, output)
  300. return err, false
  301. }
  302. }
  303. d.FsckXfsFs(fpath)
  304. cmds = [][]string{{"mkdir", "-p", tmpPoint},
  305. {"mount", fpath, tmpPoint},
  306. {"sleep", "2"},
  307. {"xfs_growfs", tmpPoint},
  308. {"sleep", "2"},
  309. {"umount", tmpPoint},
  310. {"sleep", "2"},
  311. {"rm", "-fr", tmpPoint}}
  312. } else {
  313. cmds = [][]string{{"xfs_growfs", fpath}}
  314. }
  315. } else if fs == "ntfs" {
  316. // the following cmds may cause disk damage on Windows 10 with new version of NTFS
  317. // comment out the following codes only impact Windows 2003
  318. // as windows 2003 deprecated, so choose to sacrifies windows 2003
  319. // cmds = [][]string{{"ntfsresize", "-c", fpath}, {"ntfsresize", "-P", "-f", fpath}}
  320. } else if fs == "f2fs" {
  321. if !onlineResize {
  322. cmds = [][]string{{"resize.f2fs", fpath}}
  323. }
  324. }
  325. if len(cmds) > 0 {
  326. for _, cmd := range cmds {
  327. output, err := d.Exec(cmd[0], cmd[1:]...)
  328. if err != nil {
  329. log.Errorf("resize partition: %s, %s", err, output)
  330. if raiseError {
  331. return err, false
  332. } else {
  333. return nil, false
  334. }
  335. }
  336. }
  337. }
  338. return nil, true
  339. }
  340. func (d *SFsutilDriver) FsckExtFs(fpath string) bool {
  341. log.Debugf("Exec command: %v", []string{"e2fsck", "-f", "-p", fpath})
  342. retCode, stdout, stderr, err := d.ExecInputWait("e2fsck", []string{"-f", "-p", fpath}, nil)
  343. if err != nil {
  344. log.Errorf("exec e2fsck error %s retcode %d stdout %s stderr %s", err, retCode, stdout, stderr)
  345. if retCode >= 4 {
  346. return false
  347. }
  348. }
  349. if retCode < 4 {
  350. return true
  351. }
  352. log.Errorf("failed e2fsck retcode %d %s %s", retCode, stdout, stderr)
  353. return false
  354. }
  355. // https://bugs.launchpad.net/ubuntu/+source/xfsprogs/+bug/1718244
  356. // use xfs_repair -n instead
  357. func (d *SFsutilDriver) FsckXfsFs(fpath string) bool {
  358. if output, err := d.Exec("xfs_check", fpath); err != nil {
  359. log.Errorf("xfs_check failed: %s, %s, try xfs_repair -n <dev> instead", err, output)
  360. if output, err := procutils.NewCommand("xfs_repair", "-n", fpath).Output(); err != nil {
  361. log.Errorf("xfs_repair -n dev failed: %s, %s", err, output)
  362. // repair the xfs
  363. d.Exec("xfs_repair", fpath)
  364. return false
  365. }
  366. }
  367. return true
  368. }
  369. func Mkpartition(imagePath, fsFormat string) error {
  370. var (
  371. parted = "parted"
  372. labelType = "gpt"
  373. diskType = fileutils2.FsFormatToDiskType(fsFormat)
  374. )
  375. if len(diskType) == 0 {
  376. return fmt.Errorf("Unknown fsFormat %s", fsFormat)
  377. }
  378. // 创建一个新磁盘分区表类型, ex: mbr gpt msdos ...
  379. output, err := procutils.NewCommand(parted, "-s", imagePath, "mklabel", labelType).Output()
  380. if err != nil {
  381. log.Errorf("mklabel %s %s error: %s, %s", imagePath, fsFormat, err, output)
  382. return errors.Wrapf(err, "parted mklabel failed: %s", output)
  383. }
  384. // 创建一个part-type类型的分区, part-type可以是:"primary", "logical", "extended"
  385. // 如果指定fs-type(即diskType)则在创建分区的同时进行格式化
  386. output, err = procutils.NewCommand(parted, "-s", "-a", "cylinder",
  387. imagePath, "mkpart", "primary", diskType, "0", "100%").Output()
  388. if err != nil {
  389. log.Errorf("mkpart %s %s error: %s, %s", imagePath, fsFormat, err, output)
  390. return errors.Wrapf(err, "parted mkpart failed: %s", output)
  391. }
  392. return nil
  393. }
  394. func ext4UsageType(path string) string {
  395. out, err := procutils.NewCommand("blockdev", "--getsize64", path).Output()
  396. if err != nil {
  397. log.Errorf("failed get blockdev %s size: %s", path, err)
  398. return ""
  399. }
  400. size, err := strconv.ParseInt(strings.TrimSpace(string(out)), 10, 64)
  401. if err != nil {
  402. log.Errorf("failed parse blocksize %s", out)
  403. return ""
  404. }
  405. if size > ext4UsageTypeLargefileSize {
  406. // node_ratio 1M
  407. return "largefile"
  408. } else if size > ext4UsageTypeHugefileSize {
  409. // node_ratio 64K
  410. return "huge"
  411. }
  412. return ""
  413. }
  414. func FormatPartition(path, fs, uuid string, fsFeatures *apis.FsFeatures) error {
  415. var cmd, cmdUuid []string
  416. switch {
  417. case fs == "swap":
  418. cmd = []string{"mkswap", "-U", uuid}
  419. case fs == "ext2":
  420. cmd = []string{"mkfs.ext2"}
  421. cmdUuid = []string{"tune2fs", "-U", uuid}
  422. case fs == "ext3":
  423. cmd = []string{"mkfs.ext3"}
  424. cmdUuid = []string{"tune2fs", "-U", uuid}
  425. case fs == "ext4":
  426. feature := "^64bit"
  427. extendOpts := "lazy_itable_init=1"
  428. featureOpts := []string{}
  429. if fsFeatures != nil && fsFeatures.Ext4 != nil {
  430. if fsFeatures.Ext4.CaseInsensitive {
  431. feature = fmt.Sprintf("%s,casefold,project,quota", feature)
  432. //feature = fmt.Sprintf("casefold,project,quota")
  433. extendOpts = fmt.Sprintf("%s,nodiscard,lazy_journal_init=1,encoding_flags=strict,encoding=utf8-12.1", extendOpts)
  434. }
  435. if fsFeatures.Ext4.ReservedBlocksPercentage > 0 {
  436. featureOpts = append(featureOpts, []string{"-m", fmt.Sprintf("%d", fsFeatures.Ext4.ReservedBlocksPercentage)}...)
  437. }
  438. }
  439. cmd = []string{"mkfs.ext4", "-O", feature, "-E", extendOpts}
  440. cmd = append(cmd, featureOpts...)
  441. log.Infof("===========format partion cmd: %#v", cmd)
  442. /*
  443. // see /etc/mke2fs.conf, default inode_ratio is 16384
  444. If this option is is not specified, mke2fs will pick a single default usage type based on the size
  445. of the filesystem to be created. If the filesystem size is less than or equal to 3 megabytes,
  446. mke2fs will use the filesystem type floppy. If the filesystem size is greater than 3 but less than
  447. or equal to 512 megabytes, mke2fs(8) will use the filesystem type small. If the filesystem size is
  448. greater than or equal to 4 terabytes but less than 16 terabytes, mke2fs(8) will use the filesystem
  449. type big. If the filesystem size is greater than or equal to 16 terabytes, mke2fs(8) will use the
  450. filesystem type huge. Otherwise, mke2fs(8) will use the default filesystem type default.
  451. */
  452. if usageType := ext4UsageType(path); usageType != "" {
  453. cmd = append(cmd, "-T", usageType)
  454. }
  455. cmdUuid = []string{"tune2fs", "-U", uuid}
  456. case fs == "ext4dev":
  457. cmd = []string{"mkfs.ext4dev", "-E", "lazy_itable_init=1"}
  458. cmdUuid = []string{"tune2fs", "-U", uuid}
  459. case strings.HasPrefix(fs, "fat"):
  460. cmd = []string{"mkfs.msdos"}
  461. // #case fs == "ntfs":
  462. // # cmd = []string{"/sbin/mkfs.ntfs"}
  463. case fs == "xfs":
  464. cmd = []string{"mkfs.xfs", "-f", "-m", "crc=0", "-i", "projid32bit=0", "-n", "ftype=1"}
  465. cmdUuid = []string{"xfs_admin", "-U", uuid}
  466. case fs == "f2fs":
  467. cmd = []string{"mkfs.f2fs", "-U", uuid, "-O"}
  468. options := "extra_attr,inode_checksum,sb_checksum"
  469. if fsFeatures != nil && fsFeatures.F2Fs.CaseInsensitive {
  470. options += ",casefold"
  471. }
  472. cmd = append(cmd, options)
  473. if fsFeatures != nil && fsFeatures.F2Fs.CaseInsensitive {
  474. cmd = append(cmd, "-C", "utf8")
  475. }
  476. if fsFeatures != nil && fsFeatures.F2Fs.OverprovisionRatioPercentage > 0 {
  477. cmd = append(cmd, "-o", fmt.Sprintf("%d", fsFeatures.F2Fs.OverprovisionRatioPercentage))
  478. }
  479. }
  480. if len(cmd) > 0 {
  481. var cmds = cmd
  482. cmds = append(cmds, path)
  483. if output, err := procutils.NewCommand(cmds[0], cmds[1:]...).Output(); err != nil {
  484. log.Errorf("%v failed: %s, %s", cmds, err, output)
  485. return errors.Wrapf(err, "format partition failed %s", output)
  486. }
  487. if len(cmdUuid) > 0 {
  488. cmds = cmdUuid
  489. cmds = append(cmds, path)
  490. if output, err := procutils.NewCommand(cmds[0], cmds[1:]...).Output(); err != nil {
  491. log.Errorf("%v failed: %s, %s", cmds, err, output)
  492. return errors.Wrapf(err, "format partition set uuid failed %s", output)
  493. }
  494. }
  495. return nil
  496. }
  497. return fmt.Errorf("Unknown fs %s", fs)
  498. }
  499. func DetectIsUEFISupport(rootfs fsdriver.IRootFsDriver, partitions []fsdriver.IDiskPartition) bool {
  500. for i := 0; i < len(partitions); i++ {
  501. if partitions[i].IsMounted() {
  502. if rootfs.DetectIsUEFISupport(partitions[i]) {
  503. return true
  504. }
  505. } else {
  506. if partitions[i].Mount() {
  507. support := rootfs.DetectIsUEFISupport(partitions[i])
  508. if err := partitions[i].Umount(); err != nil {
  509. log.Errorf("failed umount %s: %s", partitions[i].GetPartDev(), err)
  510. }
  511. if support {
  512. return true
  513. }
  514. }
  515. }
  516. }
  517. return false
  518. }
  519. func DetectIsBIOSSupport(partDev string, rootfs fsdriver.IRootFsDriver) bool {
  520. fi, err := os.OpenFile(partDev, os.O_RDONLY, 0444)
  521. if err != nil {
  522. log.Errorf("failed open partdev %s: %s", partDev, err)
  523. return false
  524. }
  525. defer fi.Close()
  526. // read first sector
  527. sector := make([]byte, 512)
  528. n, err := fi.Read(sector)
  529. if err != nil || n != 512 {
  530. log.Errorf("failed read first sector %d %s: %s", n, partDev, err)
  531. return false
  532. }
  533. bootSignature := sector[510:512]
  534. expectedSignature := []byte{0x55, 0xAA}
  535. log.Infof("Detect is bios support bootSignature: %x", bootSignature)
  536. if bootSignature[0] != expectedSignature[0] || bootSignature[1] != expectedSignature[1] {
  537. return false
  538. }
  539. partitionType := rootfs.GetPartition().GetPhysicalPartitionType()
  540. if partitionType == "mbr" {
  541. return true
  542. } else if partitionType == "gpt" {
  543. /*
  544. Number Start (sector) End (sector) Size Code Name
  545. 1 227328 83886046 39.9 GiB 8300
  546. 14 2048 10239 4.0 MiB EF02
  547. 15 10240 227327 106.0 MiB EF00
  548. # EF02 is BIOS Boot partition
  549. */
  550. out, err := procutils.NewCommand("sgdisk", "-p", partDev).Output()
  551. if err != nil {
  552. log.Errorf("sgdisk -p %s failed: %s, %s", partDev, err, out)
  553. return false
  554. }
  555. re := regexp.MustCompile(`^\s*(\d+)\s+(\d+)\s+(\d+)\s+([\d\.]+\s+\w+)\s+(\w+)\s*(.*)$`)
  556. scanner := bufio.NewScanner(bytes.NewReader(out))
  557. for scanner.Scan() {
  558. line := scanner.Text()
  559. m := re.FindStringSubmatch(line)
  560. if m == nil {
  561. continue
  562. }
  563. code := m[5]
  564. if code == "EF02" {
  565. return true
  566. }
  567. }
  568. }
  569. return false
  570. }
  571. func MountRootfs(readonly bool, partitions []fsdriver.IDiskPartition) (fsdriver.IRootFsDriver, error) {
  572. errs := []error{}
  573. for i := 0; i < len(partitions); i++ {
  574. log.Infof("detect partition %s", partitions[i].GetPartDev())
  575. mountFunc := partitions[i].Mount
  576. if readonly {
  577. mountFunc = partitions[i].MountPartReadOnly
  578. }
  579. if mountFunc() {
  580. fs, err := guestfs.DetectRootFs(partitions[i])
  581. if err == nil {
  582. log.Infof("Use rootfs %s, partition %s", fs, partitions[i].GetPartDev())
  583. return fs, nil
  584. }
  585. errs = append(errs, err)
  586. if err := partitions[i].Umount(); err != nil {
  587. log.Errorf("failed umount %s: %s", partitions[i].GetPartDev(), err)
  588. }
  589. }
  590. }
  591. if len(partitions) == 0 {
  592. return nil, errors.Wrap(errors.ErrNotFound, "not found any partition")
  593. }
  594. var err error = errors.ErrNotFound
  595. if len(errs) > 0 {
  596. err = errors.Wrapf(errors.ErrNotFound, "%s", errors.NewAggregate(errs).Error())
  597. }
  598. return nil, err
  599. }
  600. func DeployGuestfs(d deploy_iface.IDeployer, req *apis.DeployParams) (res *apis.DeployGuestFsResponse, err error) {
  601. root, err := d.MountRootfs(false)
  602. if err != nil {
  603. if errors.Cause(err) == errors.ErrNotFound && req.DeployInfo.IsInit {
  604. // if init deploy, ignore no partition error
  605. log.Errorf("disk.MountRootfs not found partition, not init, quit")
  606. return nil, nil
  607. }
  608. log.Errorf("Failed mounting rootfs for %s disk: %s", req.GuestDesc.Hypervisor, err)
  609. return nil, err
  610. }
  611. defer d.UmountRootfs(root)
  612. ret, err := guestfs.DoDeployGuestFs(root, req.GuestDesc, req.DeployInfo)
  613. if err != nil {
  614. log.Errorf("guestfs.DoDeployGuestFs fail %s", err)
  615. return nil, err
  616. }
  617. if ret == nil {
  618. log.Errorf("guestfs.DoDeployGuestFs return empty results")
  619. return nil, nil
  620. }
  621. return ret, nil
  622. }
  623. func ResizeFs(d deploy_iface.IDeployer, diskId string) (*apis.Empty, error) {
  624. unmount := func(root fsdriver.IRootFsDriver) error {
  625. err := d.UmountRootfs(root)
  626. if err != nil {
  627. return errors.Wrap(err, "unmount rootfs")
  628. }
  629. return nil
  630. }
  631. var rootPartDev string
  632. root, err := d.MountRootfs(false)
  633. if err != nil && errors.Cause(err) != errors.ErrNotFound {
  634. return new(apis.Empty), errors.Wrapf(err, "disk.MountRootfs")
  635. } else if err == nil {
  636. rootPartDev = root.GetPartition().GetPartDev()
  637. if !root.IsResizeFsPartitionSupport() {
  638. err := unmount(root)
  639. if err != nil {
  640. return new(apis.Empty), err
  641. }
  642. return new(apis.Empty), errors.ErrNotSupported
  643. }
  644. // must umount rootfs before resize partition
  645. err = unmount(root)
  646. if err != nil {
  647. return new(apis.Empty), err
  648. }
  649. }
  650. err = d.ResizePartition(diskId, rootPartDev)
  651. if err != nil {
  652. return new(apis.Empty), errors.Wrap(err, "resize disk partition")
  653. }
  654. return new(apis.Empty), nil
  655. }
  656. func FormatFs(d deploy_iface.IDeployer, req *apis.FormatFsParams) (*apis.Empty, error) {
  657. err := d.MakePartition(req.FsFormat)
  658. if err != nil {
  659. return new(apis.Empty), errors.Wrap(err, "MakePartition")
  660. }
  661. err = d.FormatPartition(req.FsFormat, req.Uuid, req.FsFeatures)
  662. if err != nil {
  663. return new(apis.Empty), errors.Wrap(err, "FormatPartition")
  664. }
  665. return new(apis.Empty), nil
  666. }
  667. func SaveToGlance(d deploy_iface.IDeployer, req *apis.SaveToGlanceParams) (*apis.SaveToGlanceResponse, error) {
  668. var (
  669. osInfo string
  670. relInfo *apis.ReleaseInfo
  671. )
  672. ret := &apis.SaveToGlanceResponse{
  673. OsInfo: osInfo,
  674. ReleaseInfo: relInfo,
  675. }
  676. root, err := d.MountRootfs(false)
  677. if err != nil {
  678. if errors.Cause(err) == errors.ErrNotFound {
  679. return ret, nil
  680. }
  681. return ret, errors.Wrapf(err, "MountKvmRootfs")
  682. }
  683. defer d.UmountRootfs(root)
  684. osInfo = root.GetOs()
  685. relInfo = root.GetReleaseInfo(root.GetPartition())
  686. if req.Compress {
  687. err = root.PrepareFsForTemplate(root.GetPartition())
  688. if err != nil {
  689. log.Errorf("PrepareFsForTemplate %s", err)
  690. }
  691. }
  692. if req.Compress {
  693. d.Zerofree()
  694. }
  695. return ret, err
  696. }
  697. func ProbeImageInfo(d deploy_iface.IDeployer) (*apis.ImageInfo, error) {
  698. // Fsck is executed during mount
  699. rootfs, err := d.MountRootfs(false)
  700. if err != nil {
  701. if errors.Cause(err) == errors.ErrNotFound {
  702. return new(apis.ImageInfo), nil
  703. }
  704. return new(apis.ImageInfo), errors.Wrapf(err, "d.MountKvmRootfs")
  705. }
  706. partition := rootfs.GetPartition()
  707. imageInfo := &apis.ImageInfo{
  708. OsInfo: rootfs.GetReleaseInfo(partition),
  709. OsType: rootfs.GetOs(),
  710. IsLvmPartition: d.IsLVMPartition(),
  711. IsReadonly: partition.IsReadonly(),
  712. IsInstalledCloudInit: rootfs.IsCloudinitInstall(),
  713. }
  714. d.UmountRootfs(rootfs)
  715. // In case of deploy driver is guestfish, we can't mount
  716. // multi partition concurrent, so we need umount rootfs first
  717. imageInfo.IsUefiSupport = d.DetectIsUEFISupport(rootfs)
  718. imageInfo.PhysicalPartitionType = partition.GetPhysicalPartitionType()
  719. imageInfo.IsBiosSupport = d.DetectIsBIOSSupport(rootfs)
  720. log.Infof("ProbeImageInfo response %s", imageInfo)
  721. return imageInfo, nil
  722. }