gopkg.in/essentialkaos/ek.v3@v3.5.1/fsutil/fs.go (about) 1 // +build !windows 2 3 // Package fsutil provides methods for working with files on posix compatible systems 4 package fsutil 5 6 // ////////////////////////////////////////////////////////////////////////////////// // 7 // // 8 // Copyright (c) 2009-2016 Essential Kaos // 9 // Essential Kaos Open Source License <http://essentialkaos.com/ekol?en> // 10 // // 11 // ////////////////////////////////////////////////////////////////////////////////// // 12 13 import ( 14 "errors" 15 "os" 16 "strings" 17 "syscall" 18 "time" 19 20 PATH "pkg.re/essentialkaos/ek.v3/path" 21 "pkg.re/essentialkaos/ek.v3/system" 22 ) 23 24 // ////////////////////////////////////////////////////////////////////////////////// // 25 26 const ( 27 _IFMT = 0170000 28 _IFSOCK = 0140000 29 _IFLNK = 0120000 30 _IFREG = 0100000 31 _IFBLK = 0060000 32 _IFDIR = 0040000 33 _IFCHR = 0020000 34 _IRUSR = 00400 35 _IWUSR = 00200 36 _IXUSR = 00100 37 _IRGRP = 00040 38 _IWGRP = 00020 39 _IXGRP = 00010 40 _IROTH = 00004 41 _IWOTH = 00002 42 _IXOTH = 00001 43 ) 44 45 // ////////////////////////////////////////////////////////////////////////////////// // 46 47 // CheckPerms check many props at once. 48 // 49 // F - is file 50 // D - is directory 51 // X - is executable 52 // L - is link 53 // W - is writable 54 // R - is readable 55 // S - not empty (only for files) 56 // 57 func CheckPerms(props, path string) bool { 58 if len(props) == 0 || path == "" { 59 return false 60 } 61 62 path = PATH.Clean(path) 63 props = strings.ToUpper(props) 64 65 var stat = &syscall.Stat_t{} 66 67 err := syscall.Stat(path, stat) 68 69 if err != nil { 70 return false 71 } 72 73 var user *system.User 74 75 for _, k := range props { 76 switch k { 77 78 case 'F': 79 if stat.Mode&_IFMT != _IFREG { 80 return false 81 } 82 83 case 'D': 84 if stat.Mode&_IFMT != _IFDIR { 85 return false 86 } 87 88 case 'L': 89 if stat.Mode&_IFMT != _IFLNK { 90 return false 91 } 92 93 case 'X': 94 if user == nil { 95 user, err = system.CurrentUser() 96 97 if err != nil { 98 return false 99 } 100 } 101 102 if !isExecutableStat(stat, user.UID, getGIDList(user)) { 103 return false 104 } 105 106 case 'W': 107 if user == nil { 108 user, err = system.CurrentUser() 109 110 if err != nil { 111 return false 112 } 113 } 114 115 if !isWritableStat(stat, user.UID, getGIDList(user)) { 116 return false 117 } 118 119 case 'R': 120 if user == nil { 121 user, err = system.CurrentUser() 122 123 if err != nil { 124 return false 125 } 126 } 127 128 if !isReadableStat(stat, user.UID, getGIDList(user)) { 129 return false 130 } 131 132 case 'S': 133 if stat.Size == 0 { 134 return false 135 } 136 } 137 } 138 139 return true 140 } 141 142 // ProperPath return first proper path from given slice 143 func ProperPath(props string, paths []string) string { 144 for _, path := range paths { 145 path = PATH.Clean(path) 146 147 if CheckPerms(props, path) { 148 return path 149 } 150 } 151 152 return "" 153 } 154 155 // IsExist check if target is exist in fs or not 156 func IsExist(path string) bool { 157 if path == "" { 158 return false 159 } 160 161 path = PATH.Clean(path) 162 163 return syscall.Access(path, syscall.F_OK) == nil 164 } 165 166 // IsRegular check if target is regular file or not 167 func IsRegular(path string) bool { 168 path = PATH.Clean(path) 169 mode := getMode(path) 170 171 if mode == 0 { 172 return false 173 } 174 175 return mode&_IFMT == _IFREG 176 } 177 178 // IsSocket check if target is socket or not 179 func IsSocket(path string) bool { 180 path = PATH.Clean(path) 181 mode := getMode(path) 182 183 if mode == 0 { 184 return false 185 } 186 187 return mode&_IFMT == _IFSOCK 188 } 189 190 // IsBlockDevice check if target is block device or not 191 func IsBlockDevice(path string) bool { 192 path = PATH.Clean(path) 193 mode := getMode(path) 194 195 if mode == 0 { 196 return false 197 } 198 199 return mode&_IFMT == _IFBLK 200 } 201 202 // IsCharacterDevice check if target is character device or not 203 func IsCharacterDevice(path string) bool { 204 path = PATH.Clean(path) 205 mode := getMode(path) 206 207 if mode == 0 { 208 return false 209 } 210 211 return mode&_IFMT == _IFCHR 212 } 213 214 // IsDir check if target is directory or not 215 func IsDir(path string) bool { 216 path = PATH.Clean(path) 217 mode := getMode(path) 218 219 if mode == 0 { 220 return false 221 } 222 223 return mode&_IFMT == _IFDIR 224 } 225 226 // IsLink check if file is link or not 227 func IsLink(path string) bool { 228 path = PATH.Clean(path) 229 mode := getMode(path) 230 231 if mode == 0 { 232 return false 233 } 234 235 return mode&_IFMT == _IFLNK 236 } 237 238 // IsReadable check if file is readable or not 239 func IsReadable(path string) bool { 240 if path == "" { 241 return false 242 } 243 244 path = PATH.Clean(path) 245 246 var stat = &syscall.Stat_t{} 247 248 err := syscall.Stat(path, stat) 249 250 if err != nil { 251 return false 252 } 253 254 user, err := system.CurrentUser() 255 256 if err != nil { 257 return false 258 } 259 260 return isReadableStat(stat, user.UID, getGIDList(user)) 261 } 262 263 // IsWritable check if file is writable or not 264 func IsWritable(path string) bool { 265 if path == "" { 266 return false 267 } 268 269 path = PATH.Clean(path) 270 271 var stat = &syscall.Stat_t{} 272 273 err := syscall.Stat(path, stat) 274 275 if err != nil { 276 return false 277 } 278 279 user, err := system.CurrentUser() 280 281 if err != nil { 282 return false 283 } 284 285 return isWritableStat(stat, user.UID, getGIDList(user)) 286 } 287 288 // IsExecutable check if file is executable or not 289 func IsExecutable(path string) bool { 290 if path == "" { 291 return false 292 } 293 294 path = PATH.Clean(path) 295 296 var stat = &syscall.Stat_t{} 297 298 err := syscall.Stat(path, stat) 299 300 if err != nil { 301 return false 302 } 303 304 user, err := system.CurrentUser() 305 306 if err != nil { 307 return false 308 } 309 310 return isExecutableStat(stat, user.UID, getGIDList(user)) 311 } 312 313 // IsNonEmpty check if file is empty or not 314 func IsNonEmpty(path string) bool { 315 if path == "" { 316 return false 317 } 318 319 path = PATH.Clean(path) 320 321 return GetSize(path) != 0 322 } 323 324 // IsEmptyDir check if directory empty or not 325 func IsEmptyDir(path string) bool { 326 if path == "" { 327 return false 328 } 329 330 path = PATH.Clean(path) 331 332 fd, err := syscall.Open(path, syscall.O_RDONLY, 0) 333 334 if err != nil { 335 return false 336 } 337 338 defer syscall.Close(fd) 339 340 n, err := syscall.ReadDirent(fd, make([]byte, 4096)) 341 342 if n == 0x30 || err != nil { 343 return true 344 } 345 346 return false 347 } 348 349 // GetOwner return object owner pid and gid 350 func GetOwner(path string) (int, int, error) { 351 if path == "" { 352 return -1, -1, errors.New("Path is empty") 353 } 354 355 path = PATH.Clean(path) 356 357 var stat = &syscall.Stat_t{} 358 359 err := syscall.Stat(path, stat) 360 361 if err != nil { 362 return -1, -1, err 363 } 364 365 return int(stat.Uid), int(stat.Gid), nil 366 } 367 368 // GetATime return time of last access 369 func GetATime(path string) (time.Time, error) { 370 path = PATH.Clean(path) 371 372 atime, _, _, err := GetTimes(path) 373 374 return atime, err 375 } 376 377 // GetCTime return time of creation 378 func GetCTime(path string) (time.Time, error) { 379 path = PATH.Clean(path) 380 381 _, _, ctime, err := GetTimes(path) 382 383 return ctime, err 384 } 385 386 // GetMTime return time of modification 387 func GetMTime(path string) (time.Time, error) { 388 path = PATH.Clean(path) 389 390 _, mtime, _, err := GetTimes(path) 391 392 return mtime, err 393 } 394 395 // GetSize return file size in bytes 396 func GetSize(path string) int64 { 397 if path == "" { 398 return 0 399 } 400 401 path = PATH.Clean(path) 402 403 var stat = &syscall.Stat_t{} 404 405 err := syscall.Stat(path, stat) 406 407 if err != nil { 408 return 0 409 } 410 411 return stat.Size 412 } 413 414 // GetPerm return file permissions 415 func GetPerm(path string) os.FileMode { 416 path = PATH.Clean(path) 417 return os.FileMode(getMode(path) & 0777) 418 } 419 420 // ////////////////////////////////////////////////////////////////////////////////// // 421 422 func getMode(path string) uint32 { 423 if path == "" { 424 return 0 425 } 426 427 var stat = &syscall.Stat_t{} 428 429 err := syscall.Stat(path, stat) 430 431 if err != nil { 432 return 0 433 } 434 435 return uint32(stat.Mode) 436 } 437 438 func isReadableStat(stat *syscall.Stat_t, uid int, gids []int) bool { 439 if uid == 0 { 440 return true 441 } 442 443 switch { 444 case stat.Mode&_IROTH == _IROTH: 445 return true 446 case stat.Mode&_IRUSR == _IRUSR && uid == int(stat.Uid): 447 return true 448 } 449 450 for _, gid := range gids { 451 if stat.Mode&_IRGRP == _IRGRP && gid == int(stat.Gid) { 452 return true 453 } 454 } 455 456 return false 457 } 458 459 func isWritableStat(stat *syscall.Stat_t, uid int, gids []int) bool { 460 if uid == 0 { 461 return true 462 } 463 464 switch { 465 case stat.Mode&_IWOTH == _IWOTH: 466 return true 467 case stat.Mode&_IWUSR == _IWUSR && uid == int(stat.Uid): 468 return true 469 } 470 471 for _, gid := range gids { 472 if stat.Mode&_IWGRP == _IWGRP && gid == int(stat.Gid) { 473 return true 474 } 475 } 476 477 return false 478 } 479 480 func isExecutableStat(stat *syscall.Stat_t, uid int, gids []int) bool { 481 if uid == 0 { 482 return true 483 } 484 485 switch { 486 case stat.Mode&_IXOTH == _IXOTH: 487 return true 488 case stat.Mode&_IXUSR == _IXUSR && uid == int(stat.Uid): 489 return true 490 } 491 492 for _, gid := range gids { 493 if stat.Mode&_IXGRP == _IXGRP && gid == int(stat.Gid) { 494 return true 495 } 496 } 497 498 return false 499 } 500 501 func getGIDList(user *system.User) []int { 502 if user == nil { 503 return []int{} 504 } 505 506 var result []int 507 508 for _, group := range user.Groups { 509 result = append(result, group.GID) 510 } 511 512 return result 513 }