fileutils.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  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 fileutils2
  15. import (
  16. "archive/tar"
  17. "bufio"
  18. "bytes"
  19. "compress/gzip"
  20. "fmt"
  21. "io"
  22. "io/ioutil"
  23. "os"
  24. "path"
  25. "path/filepath"
  26. "regexp"
  27. "strconv"
  28. "strings"
  29. "yunion.io/x/log"
  30. "yunion.io/x/pkg/errors"
  31. "yunion.io/x/pkg/util/regutils"
  32. "yunion.io/x/onecloud/pkg/util/procutils"
  33. )
  34. func Cleandir(sPath string, keepdir bool) error {
  35. if f, _ := os.Lstat(sPath); f == nil || f.Mode()&os.ModeSymlink == os.ModeSymlink {
  36. return nil
  37. }
  38. files, _ := ioutil.ReadDir(sPath)
  39. for _, file := range files {
  40. fp := path.Join(sPath, file.Name())
  41. if f, _ := os.Lstat(fp); f.Mode()&os.ModeSymlink == os.ModeSymlink {
  42. if !keepdir {
  43. if err := os.Remove(fp); err != nil {
  44. return err
  45. }
  46. }
  47. } else if f.IsDir() {
  48. Cleandir(fp, keepdir)
  49. if !keepdir {
  50. if err := os.Remove(fp); err != nil {
  51. return err
  52. }
  53. }
  54. } else {
  55. if err := os.Remove(fp); err != nil {
  56. return err
  57. }
  58. }
  59. }
  60. return nil
  61. }
  62. // TODO: test
  63. func Zerofiles(sPath string) error {
  64. f, err := os.Lstat(sPath)
  65. switch {
  66. case err != nil:
  67. return err
  68. case f.Mode()&os.ModeSymlink == os.ModeSymlink:
  69. // islink
  70. return nil
  71. case f.Mode().IsRegular():
  72. return FilePutContents(sPath, "", false)
  73. case f.Mode().IsDir():
  74. files, err := ioutil.ReadDir(sPath)
  75. if err != nil {
  76. return err
  77. }
  78. for _, file := range files {
  79. if file.Mode()&os.ModeSymlink == os.ModeSymlink {
  80. continue
  81. } else if file.Mode().IsRegular() {
  82. if err := FilePutContents(path.Join(sPath, file.Name()), "", false); err != nil {
  83. return err
  84. }
  85. } else if file.Mode().IsDir() {
  86. return Zerofiles(path.Join(sPath, file.Name()))
  87. }
  88. }
  89. }
  90. return nil
  91. }
  92. func FileSetContents(filename string, content string) error {
  93. return FilePutContents(filename, content, false)
  94. }
  95. func FileAppendContents(filename string, content string) error {
  96. return FilePutContents(filename, content, true)
  97. }
  98. func FilePutContents(filename string, content string, modAppend bool) error {
  99. var mode = os.O_WRONLY | os.O_CREATE
  100. if modAppend {
  101. mode = mode | os.O_APPEND
  102. } else {
  103. mode = mode | os.O_TRUNC
  104. }
  105. fd, err := os.OpenFile(filename, mode, 0644)
  106. if err != nil {
  107. return err
  108. }
  109. defer fd.Close()
  110. _, err = fd.WriteString(content)
  111. return err
  112. }
  113. func IsBlockDevMounted(dev string) bool {
  114. devPath := "/dev/" + dev
  115. mounts, err := procutils.NewCommand("mount").Output()
  116. if err != nil {
  117. return false
  118. }
  119. for _, s := range strings.Split(string(mounts), "\n") {
  120. if strings.HasPrefix(s, devPath) {
  121. return true
  122. }
  123. }
  124. return false
  125. }
  126. func IsBlockDeviceUsed(dev string) bool {
  127. if strings.HasPrefix(dev, "/dev/") {
  128. dev = dev[strings.LastIndex(dev, "/")+1:]
  129. }
  130. devStr := fmt.Sprintf(" %s\n", dev)
  131. devs, _ := procutils.NewCommand("cat", "/proc/partitions").Output()
  132. if idx := strings.Index(string(devs), devStr); idx > 0 {
  133. return true
  134. }
  135. return false
  136. }
  137. func GetAllBlkdevsIoSchedulers() ([]string, error) {
  138. if _, err := os.Stat("/sys/block"); !os.IsNotExist(err) {
  139. blockDevs, err := os.ReadDir("/sys/block")
  140. if err != nil {
  141. log.Errorf("ReadDir /sys/block error: %s", err)
  142. return nil, errors.Wrap(err, "ioutil.ReadDir(/sys/block)")
  143. }
  144. for _, b := range blockDevs {
  145. // check is a block device
  146. if !Exists(path.Join("/sys/block", b.Name(), "device")) {
  147. continue
  148. }
  149. if IsBlockDevMounted(b.Name()) {
  150. conf, err := GetBlkdevConfig(b.Name(), "queue/scheduler")
  151. if err != nil {
  152. log.Errorf("Get %s queue/scheduler fail %s", b.Name(), err)
  153. } else {
  154. algs := make([]string, 0)
  155. for _, alg := range strings.Split(strings.TrimSpace(conf), " ") {
  156. if len(alg) > 0 {
  157. if alg[0] == '[' {
  158. alg = alg[1 : len(alg)-1]
  159. }
  160. algs = append(algs, alg)
  161. }
  162. }
  163. return algs, nil
  164. }
  165. }
  166. }
  167. log.Errorf("no block device avaiable")
  168. return nil, nil
  169. } else {
  170. log.Errorf("stat /sys/block fail %s", err)
  171. return nil, errors.Wrap(err, "stat /sys/block fail")
  172. }
  173. }
  174. func ChangeAllBlkdevsParams(params map[string]string) {
  175. if _, err := os.Stat("/sys/block"); !os.IsNotExist(err) {
  176. blockDevs, err := ioutil.ReadDir("/sys/block")
  177. if err != nil {
  178. log.Errorf("ReadDir /sys/block error: %s", err)
  179. return
  180. }
  181. for _, b := range blockDevs {
  182. if !Exists(path.Join("/sys/block", b.Name(), "device")) {
  183. continue
  184. }
  185. for k, v := range params {
  186. ChangeBlkdevParameter(b.Name(), k, v)
  187. }
  188. }
  189. }
  190. }
  191. func BlockDevIsSsd(dev string) bool {
  192. rotational := path.Join("/sys/block", dev, "queue", "rotational")
  193. res, err := FileGetContents(rotational)
  194. if err != nil {
  195. log.Errorf("FileGetContents fail %s %s", rotational, err)
  196. return false
  197. }
  198. return strings.TrimSpace(res) == "0"
  199. }
  200. func ChangeSsdBlkdevsParams(params map[string]string) {
  201. if _, err := os.Stat("/sys/block"); !os.IsNotExist(err) {
  202. blockDevs, err := os.ReadDir("/sys/block")
  203. if err != nil {
  204. log.Errorf("ReadDir /sys/block error: %s", err)
  205. return
  206. }
  207. for _, b := range blockDevs {
  208. if !Exists(path.Join("/sys/block", b.Name(), "device")) {
  209. continue
  210. }
  211. if !BlockDevIsSsd(b.Name()) {
  212. continue
  213. }
  214. for k, v := range params {
  215. ChangeBlkdevParameter(b.Name(), k, v)
  216. }
  217. }
  218. }
  219. }
  220. func ChangeHddBlkdevsParams(params map[string]string) {
  221. if _, err := os.Stat("/sys/block"); !os.IsNotExist(err) {
  222. blockDevs, err := os.ReadDir("/sys/block")
  223. if err != nil {
  224. log.Errorf("ReadDir /sys/block error: %s", err)
  225. return
  226. }
  227. for _, b := range blockDevs {
  228. if !Exists(path.Join("/sys/block", b.Name(), "device")) {
  229. continue
  230. }
  231. if BlockDevIsSsd(b.Name()) {
  232. continue
  233. }
  234. for k, v := range params {
  235. ChangeBlkdevParameter(b.Name(), k, v)
  236. }
  237. }
  238. }
  239. }
  240. func ChangeBlkdevParameter(dev, key, value string) {
  241. p := path.Join("/sys/block", dev, key)
  242. if _, err := os.Stat(p); !os.IsNotExist(err) {
  243. err = FilePutContents(p, value, false)
  244. if err != nil {
  245. log.Errorf("Fail to set %s of %s to %s:%s", key, dev, value, err)
  246. }
  247. log.Infof("Set %s of %s to %s", key, dev, value)
  248. }
  249. }
  250. func GetBlkdevConfig(dev, key string) (string, error) {
  251. p := path.Join("/sys/block", dev, key)
  252. if _, err := os.Stat(p); !os.IsNotExist(err) {
  253. return FileGetContents(p)
  254. } else {
  255. return "", err
  256. }
  257. }
  258. func FileGetContents(file string) (string, error) {
  259. content, err := ioutil.ReadFile(file)
  260. if err != nil {
  261. return "", err
  262. }
  263. return string(content), nil
  264. }
  265. func FileGetIntContent(file string) (int, error) {
  266. content, err := FileGetContents(file)
  267. if err != nil {
  268. return -1, errors.Wrap(err, "FileGetContents")
  269. }
  270. val, err := strconv.Atoi(strings.TrimSpace(content))
  271. if err != nil {
  272. return -1, errors.Wrapf(err, "convert %s to int", content)
  273. }
  274. return val, nil
  275. }
  276. func GetFsFormat(diskPath string) string {
  277. ret, err := procutils.NewCommand("blkid", "-o", "value", "-s", "TYPE", diskPath).Output()
  278. if err != nil {
  279. log.Errorf("failed exec blkid of dev %s: %s, %s", diskPath, err, ret)
  280. return ""
  281. }
  282. var res string
  283. for _, line := range strings.Split(string(ret), "\n") {
  284. res += line
  285. }
  286. return res
  287. }
  288. func CleanFailedMountpoints() {
  289. var mtfile = "/etc/mtab"
  290. if _, err := os.Stat(mtfile); os.IsNotExist(err) {
  291. mtfile = "/proc/mounts"
  292. }
  293. f, err := os.Open(mtfile)
  294. if err != nil {
  295. log.Errorf("CleanFailedMountpoints error: %s", err)
  296. }
  297. reader := bufio.NewReader(f)
  298. line, _, err := reader.ReadLine()
  299. for err != nil {
  300. m := strings.Split(string(line), " ")
  301. if len(m) > 1 {
  302. mp := m[1]
  303. if _, err := os.Stat(mp); os.IsNotExist(err) {
  304. log.Warningf("Mount point %s not exists", mp)
  305. }
  306. procutils.NewCommand("umount", mp).Run()
  307. }
  308. }
  309. }
  310. type HostsFile map[string][]string
  311. func (hf HostsFile) Parse(content string) {
  312. lines := strings.Split(content, "\n")
  313. for _, line := range lines {
  314. data := regexp.MustCompile(`\s+`).Split(line, -1)
  315. for len(data) > 0 && data[len(data)-1] == "" {
  316. data = data[:len(data)-1]
  317. }
  318. if len(data) > 1 {
  319. hf[data[0]] = data[1:]
  320. }
  321. }
  322. }
  323. func (hf HostsFile) Add(name string, value ...string) {
  324. hf[name] = value
  325. }
  326. func (hf HostsFile) String() string {
  327. var ret = ""
  328. for k, v := range hf {
  329. if len(v) > 0 {
  330. ret += fmt.Sprintf("%s\t%s\n", k, strings.Join(v, "\t"))
  331. }
  332. }
  333. return ret
  334. }
  335. func FormatHostsFile(content string, ips []string, hostname, hostdomain string) string {
  336. hf := make(HostsFile, 0)
  337. hf.Parse(content)
  338. hf.Add("127.0.0.1", "localhost")
  339. isV6 := false
  340. for _, ip := range ips {
  341. if regutils.MatchIP6Addr(ip) {
  342. isV6 = true
  343. }
  344. }
  345. if isV6 {
  346. hf.Add("::1", "localhost", "ip6-localhost", "ip6-loopback")
  347. }
  348. for _, ip := range ips {
  349. hf.Add(ip, hostdomain, hostname)
  350. }
  351. return hf.String()
  352. }
  353. func FsFormatToDiskType(fsFormat string) string {
  354. switch {
  355. case fsFormat == "swap":
  356. return "linux-swap"
  357. case strings.HasPrefix(fsFormat, "ext") || fsFormat == "xfs" || fsFormat == "f2fs":
  358. return "ext2"
  359. case strings.HasPrefix(fsFormat, "fat"):
  360. return "fat32"
  361. case fsFormat == "ntfs":
  362. return fsFormat
  363. default:
  364. return ""
  365. }
  366. }
  367. func GetDevOfPath(spath string) string {
  368. spath, err := filepath.Abs(spath)
  369. if err != nil {
  370. log.Errorln(err)
  371. return ""
  372. }
  373. lines, err := procutils.NewCommand("mount").Output()
  374. if err != nil {
  375. log.Errorln(err)
  376. return ""
  377. }
  378. var (
  379. maxMatchLen int
  380. matchDev string
  381. )
  382. for _, line := range strings.Split(string(lines), "\n") {
  383. segs := strings.Split(line, " ")
  384. if len(segs) < 3 {
  385. continue
  386. }
  387. if strings.HasPrefix(segs[0], "/dev/") {
  388. if strings.HasPrefix(spath, segs[2]) {
  389. matchLen := len(segs[2])
  390. if maxMatchLen < matchLen {
  391. maxMatchLen = matchLen
  392. matchDev = segs[0]
  393. }
  394. }
  395. }
  396. }
  397. return matchDev
  398. }
  399. func GetDevId(spath string) string {
  400. dev := GetDevOfPath(spath)
  401. if len(dev) == 0 {
  402. return ""
  403. }
  404. devInfo, err := procutils.NewCommand("ls", "-l", dev).Output()
  405. if err != nil {
  406. log.Errorln(err)
  407. return ""
  408. }
  409. devInfos := strings.Split(string(devInfo), "\n")
  410. data := strings.Split(string(devInfos[0]), " ")
  411. data[4] = data[4][:len(data[4])-1]
  412. return strings.Join(data, ":")
  413. }
  414. func GetDevUuid(dev string) (map[string]string, error) {
  415. lines, err := procutils.NewCommand("blkid", dev).Output()
  416. if err != nil {
  417. log.Errorf("GetDevUuid %s error: %v", dev, err)
  418. return map[string]string{}, errors.Wrapf(err, "blkid")
  419. }
  420. for _, l := range strings.Split(string(lines), "\n") {
  421. if strings.HasPrefix(l, dev) {
  422. var ret = map[string]string{}
  423. for _, part := range strings.Split(l, " ") {
  424. data := strings.Split(part, "=")
  425. if len(data) == 2 && strings.HasSuffix(data[0], "UUID") {
  426. if data[1][0] == '"' || data[1][0] == '\'' {
  427. ret[data[0]] = data[1][1 : len(data[1])-1]
  428. } else {
  429. ret[data[0]] = data[1]
  430. }
  431. }
  432. }
  433. return ret, nil
  434. }
  435. }
  436. return map[string]string{}, nil
  437. }
  438. func IsIsoFile(sPath string) bool {
  439. file, err := os.Open(sPath)
  440. if err != nil {
  441. return false
  442. }
  443. defer file.Close()
  444. file.Seek(0x8001, 0)
  445. buffer := make([]byte, 5)
  446. _, err = file.Read(buffer)
  447. if err != nil {
  448. return false
  449. }
  450. return bytes.Equal(buffer, []byte("CD001"))
  451. }
  452. func IsTarGzipFile(fPath string) bool {
  453. f, err := os.Open(fPath)
  454. if err != nil {
  455. return false
  456. }
  457. defer f.Close()
  458. gzf, err := gzip.NewReader(f)
  459. if err != nil {
  460. return false
  461. }
  462. return IsTarStream(gzf)
  463. }
  464. func IsTarStream(f io.Reader) bool {
  465. tarReader := tar.NewReader(f)
  466. _, err := tarReader.Next()
  467. if err != nil {
  468. return false
  469. }
  470. return true
  471. }
  472. func IsTarFile(fPath string) bool {
  473. f, err := os.Open(fPath)
  474. if err != nil {
  475. return false
  476. }
  477. defer f.Close()
  478. return IsTarStream(f)
  479. }