github.com/razvanm/vanadium-go-1.3@v0.0.0-20160721203343-4a65068e5915/src/runtime/ppapi/file_nacl.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ppapi 6 7 import ( 8 "errors" 9 "fmt" 10 "io" 11 "os" 12 "time" 13 "unsafe" 14 ) 15 16 var ( 17 errFileIOCreateFailed = errors.New("FileIO creation failed") 18 errFileSystemCreateFailed = errors.New("filesystem creation failed") 19 errFileRefParentFailed = errors.New("FileRef.Parent failed") 20 errNegativeFilePosition = errors.New("negative file position") 21 ) 22 23 // FileSystem specifies the file system type associated with a file. For 24 // example, the filesystem specifies whether a file is persistent or temporary. 25 type FileSystem struct { 26 Resource 27 instance Instance 28 } 29 30 // Type returns the type of the file system. 31 func (fs FileSystem) Type() FileSystemType { 32 return ppb_filesystem_get_type(fs.id) 33 } 34 35 // OpenFS opens the file system. 36 // 37 // A file system must be opened before running any other operation on it. 38 // 39 // Note that this does not request quota; to do that, you must either invoke 40 // requestQuota from JavaScript: 41 // http://www.html5rocks.com/en/tutorials/file/filesystem/#toc-requesting-quota 42 // or set the unlimitedStorage permission for Chrome Web Store apps: 43 // http://code.google.com/chrome/extensions/manifest.html#permissions. 44 func (fs FileSystem) OpenFS(expectedSize int64) error { 45 code := ppb_filesystem_open(fs.id, expectedSize, ppNullCompletionCallback) 46 return decodeError(Error(code)) 47 } 48 49 // Remove deletes a file or directory. 50 // 51 // If the name refers to a directory, then the directory must be empty. It is an 52 // error to delete a file or directory that is in use. It is not valid to delete 53 // a file in the external file system. 54 func (fs FileSystem) Remove(name string) error { 55 ref, err := fs.CreateFileRef(name) 56 if err != nil { 57 return &os.PathError{Op: "Remove", Path: name, Err: err} 58 } 59 defer ref.Release() 60 if err := ref.Delete(); err != nil { 61 return &os.PathError{Op: "Remove", Path: name, Err: err} 62 } 63 return nil 64 } 65 66 // RemoveAll removes path and any children it contains. It removes everything it 67 // can but returns the first error it encounters. If the path does not exist, 68 // RemoveAll returns nil (no error). 69 func (fs FileSystem) RemoveAll(name string) error { 70 ref, err := fs.CreateFileRef(name) 71 if err != nil { 72 return &os.PathError{Op: "RemoveAll", Path: name, Err: err} 73 } 74 defer ref.Release() 75 76 // Stat the file. 77 info, err := ref.Stat() 78 if err != nil { 79 if err == ppErrors[PP_ERROR_FILENOTFOUND] { 80 return nil 81 } 82 return &os.PathError{Op: "RemoveAll", Path: name, Err: err} 83 } 84 85 // If it is a directory, delete the entries recursively. 86 if info.IsDir() { 87 names, err := fs.Readdirnames(name) 88 if err != nil { 89 return err 90 } 91 for _, name := range names { 92 if err := fs.RemoveAll(name); err != nil { 93 return err 94 } 95 } 96 } 97 98 // Remove the file. 99 if err := ref.Delete(); err != nil { 100 return &os.PathError{Op: "RemoveAll", Path: name, Err: err} 101 } 102 return nil 103 } 104 105 // Rename renames a file. 106 // 107 // It is an error to rename a file or directory that is in use. It is not valid 108 // to rename a file in the external file system. 109 func (fs FileSystem) Rename(fromName, toName string) error { 110 fromRef, err := fs.CreateFileRef(fromName) 111 if err != nil { 112 return err 113 } 114 defer fromRef.Release() 115 116 toRef, err := fs.CreateFileRef(toName) 117 if err != nil { 118 return err 119 } 120 defer toRef.Release() 121 122 return fromRef.Rename(toRef) 123 } 124 125 // Mkdir creates a new directory with the specified name and permission bits. If 126 // there is an error, it will be of type *PathError. 127 // 128 // It is not valid to make a directory in the external file system. 129 func (fs FileSystem) Mkdir(name string) error { 130 ref, err := fs.CreateFileRef(name) 131 if err != nil { 132 return &os.PathError{Op: "Mkdir", Path: name, Err: err} 133 } 134 defer ref.Release() 135 136 if err := ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_NONE); err != nil { 137 return &os.PathError{Op: "Mkdir", Path: name, Err: err} 138 } 139 return nil 140 } 141 142 // MkdirAll creates a directory named path, along with any necessary parents, 143 // and returns nil, or else returns an error. The permission bits perm are used 144 // for all directories that MkdirAll creates. If path is already a directory, 145 // MkdirAll does nothing and returns nil. 146 func (fs FileSystem) MkdirAll(name string) error { 147 ref, err := fs.CreateFileRef(name) 148 if err != nil { 149 return &os.PathError{Op: "Mkdir", Path: name, Err: err} 150 } 151 defer ref.Release() 152 153 if err := ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_WITH_ANCESTORS); err != nil { 154 return &os.PathError{Op: "Mkdir", Path: name, Err: err} 155 } 156 return nil 157 } 158 159 // Readdirnames reads and returns a slice of names from the directory f. 160 func (fs FileSystem) Readdirnames(name string) ([]string, error) { 161 ref, err := fs.CreateFileRef(name) 162 if err != nil { 163 return nil, &os.PathError{Op: "Readdirnames", Path: name, Err: err} 164 } 165 defer ref.Release() 166 167 entries, err := ref.ReadDirectoryEntries() 168 if err != nil { 169 return nil, &os.PathError{Op: "Readdirnames", Path: name, Err: err} 170 } 171 172 names := make([]string, len(entries)) 173 for i, entry := range entries { 174 name, err := entry.File.Path() 175 if err != nil { 176 return nil, &os.PathError{Op: "Readdirnames", Path: name, Err: err} 177 } 178 names[i] = name 179 } 180 return names, nil 181 } 182 183 // Stat queries info about a file or directory. 184 // 185 // You must have access to read this file or directory if it exists in the 186 // external filesystem. 187 func (fs FileSystem) Stat(name string) (info FileInfo, err error) { 188 ref, e := fs.CreateFileRef(name) 189 if e != nil { 190 err = &os.PathError{Op: "Stat", Path: name, Err: e} 191 return 192 } 193 defer ref.Release() 194 195 info, e = ref.Stat() 196 if e != nil { 197 err = &os.PathError{Op: "Stat", Path: name, Err: e} 198 return 199 } 200 return 201 } 202 203 // Chtimes changes the access and modification times of the named file, similar 204 // to the Unix utime() or utimes() functions. 205 // 206 // The underlying filesystem may truncate or round the values to a less precise 207 // time unit. If there is an error, it will be of type *PathError. 208 func (fs FileSystem) Chtimes(name string, atime, mtime time.Time) error { 209 ref, err := fs.CreateFileRef(name) 210 if err != nil { 211 return &os.PathError{Op: "Chtimes", Path: name, Err: err} 212 } 213 defer ref.Release() 214 if err := ref.Touch(atime, mtime); err != nil { 215 return &os.PathError{Op: "Chtimes", Path: name, Err: err} 216 } 217 return nil 218 } 219 220 // Open opens the named file for reading. If successful, methods on the returned 221 // file can be used for reading; the associated file has mode O_RDONLY. If there 222 // is an error, it will be of type *PathError. 223 func (fs FileSystem) Open(name string) (*FileIO, error) { 224 return fs.OpenFile(name, os.O_RDONLY) 225 } 226 227 // Create creates the named file mode 0666 (before umask), truncating it if it 228 // already exists. If successful, methods on the returned File can be used for 229 // I/O; the associated file has mode O_RDWR. If there is an error, it will be of 230 // type *PathError. 231 func (fs FileSystem) Create(name string) (*FileIO, error) { 232 return fs.OpenFile(name, os.O_RDWR|os.O_TRUNC|os.O_CREATE) 233 } 234 235 // OpenFile is the generalized open call; most users will use Open or Create 236 // instead. It opens the named file with specified flag (O_RDONLY etc.) and 237 // perm, (0666 etc.) if applicable. If successful, methods on the returned File 238 // can be used for I/O. If there is an error, it will be of type *PathError. 239 func (fs FileSystem) OpenFile(name string, flag int) (*FileIO, error) { 240 ref, err := fs.CreateFileRef(name) 241 if err != nil { 242 return nil, &os.PathError{Op: "CreateFileRef", Path: name, Err: err} 243 } 244 defer ref.Release() 245 246 file, err := fs.instance.CreateFileIO() 247 if err != nil { 248 return nil, &os.PathError{Op: "CreateFileIO", Path: name, Err: err} 249 } 250 251 var pflag FileOpenFlag 252 if flag&os.O_RDONLY != 0 { 253 pflag |= PP_FILEOPENFLAG_READ 254 } 255 if flag&os.O_WRONLY != 0 { 256 pflag |= PP_FILEOPENFLAG_WRITE 257 } 258 if flag&os.O_RDWR != 0 { 259 pflag |= PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_WRITE 260 } 261 if flag&os.O_CREATE != 0 { 262 pflag |= PP_FILEOPENFLAG_CREATE 263 } 264 if flag&os.O_EXCL != 0 { 265 pflag |= PP_FILEOPENFLAG_EXCLUSIVE 266 } 267 if flag&os.O_TRUNC != 0 { 268 pflag |= PP_FILEOPENFLAG_TRUNCATE 269 } 270 if flag&os.O_APPEND != 0 { 271 pflag |= PP_FILEOPENFLAG_APPEND 272 } 273 if err := file.Open(ref, pflag); err != nil { 274 file.Release() 275 return nil, &os.PathError{Op: "Open", Path: name, Err: err} 276 } 277 return &file, nil 278 } 279 280 // FileInfo represents information about a file, such as size, type, and 281 // creation time. 282 // 283 // Implements os.FileInfo. 284 type FileInfo struct { 285 Filename string 286 Len int64 287 Type FileType 288 FSType FileSystemType 289 CTime, ATime, MTime time.Time 290 } 291 292 var _ os.FileInfo = &FileInfo{} 293 294 func (in FileInfo) toPP(out *pp_FileInfo) { 295 *(*int64)(unsafe.Pointer(&out[0])) = in.Len 296 *(*FileType)(unsafe.Pointer(&out[8])) = in.Type 297 *(*FileSystemType)(unsafe.Pointer(&out[12])) = in.FSType 298 *(*pp_Time)(unsafe.Pointer(&out[16])) = toPPTime(in.CTime) 299 *(*pp_Time)(unsafe.Pointer(&out[24])) = toPPTime(in.ATime) 300 *(*pp_Time)(unsafe.Pointer(&out[32])) = toPPTime(in.MTime) 301 } 302 303 func (out *FileInfo) fromPP(name string, in pp_FileInfo) { 304 out.Filename = name 305 out.Len = *(*int64)(unsafe.Pointer(&in[0])) 306 out.Type = *(*FileType)(unsafe.Pointer(&in[8])) 307 out.FSType = *(*FileSystemType)(unsafe.Pointer(&in[12])) 308 out.CTime = fromPPTime(*(*pp_Time)(unsafe.Pointer(&in[16]))) 309 out.ATime = fromPPTime(*(*pp_Time)(unsafe.Pointer(&in[24]))) 310 out.MTime = fromPPTime(*(*pp_Time)(unsafe.Pointer(&in[32]))) 311 } 312 313 func (info *FileInfo) Name() string { 314 return info.Filename 315 } 316 317 func (info *FileInfo) Size() int64 { 318 return info.Len 319 } 320 321 func (info *FileInfo) Mode() os.FileMode { 322 return 0666 323 } 324 325 func (info *FileInfo) ModTime() time.Time { 326 return info.MTime 327 } 328 329 func (info *FileInfo) IsDir() bool { 330 return info.Type == PP_FILETYPE_DIRECTORY 331 } 332 333 func (info *FileInfo) Sys() interface{} { 334 return nil 335 } 336 337 // DirectoryEntry is an entry in a directory. 338 type DirectoryEntry struct { 339 File FileRef 340 Type FileType 341 } 342 343 func (in DirectoryEntry) toPP(out *pp_DirectoryEntry) { 344 *(*pp_Resource)(unsafe.Pointer(&out[0])) = in.File.id 345 *(*FileType)(unsafe.Pointer(&out[4])) = in.Type 346 } 347 348 func (out *DirectoryEntry) fromPP(in pp_DirectoryEntry) { 349 out.File.id = *(*pp_Resource)(unsafe.Pointer(&in[0])) 350 out.Type = *(*FileType)(unsafe.Pointer(&in[4])) 351 } 352 353 // FileRef represents a "weak pointer" to a file in a file system. 354 type FileRef struct { 355 Resource 356 } 357 358 // CreateFileRef creates a weak pointer to a file in the given file system. 359 // The returned ref must be released explicitly with the Release method. 360 func (fs FileSystem) CreateFileRef(path string) (ref FileRef, err error) { 361 b := append([]byte(path), byte(0)) 362 id := ppb_fileref_create(fs.id, &b[0]) 363 if id == 0 { 364 err = fmt.Errorf("can't create file %q", path) 365 return 366 } 367 ref.id = id 368 return 369 } 370 371 // Delete deletes a file or directory. 372 // 373 // If the ref refers to a directory, then the directory must be empty. It is an 374 // error to delete a file or directory that is in use. It is not valid to delete 375 // a file in the external file system. 376 func (ref FileRef) Delete() error { 377 code := ppb_fileref_delete(ref.id, ppNullCompletionCallback) 378 return decodeError(Error(code)) 379 } 380 381 // GetParent returns the parent directory of this file. 382 // 383 // If file_ref points to the root of the filesystem, then the root is returned. 384 func (ref FileRef) Parent() (parent FileRef, err error) { 385 id := ppb_fileref_get_parent(ref.id) 386 if id == 0 { 387 err = errFileRefParentFailed 388 return 389 } 390 parent.id = id 391 return 392 } 393 394 // Name returns the name of the file. 395 func (ref FileRef) Name() (string, error) { 396 var ppVar pp_Var 397 ppb_fileref_get_name(&ppVar, ref.id) 398 v := makeVar(ppVar) 399 s, err := v.AsString() 400 v.Release() 401 return s, err 402 } 403 404 // Path returns the full path of the file. 405 func (ref FileRef) Path() (string, error) { 406 var ppVar pp_Var 407 ppb_fileref_get_path(&ppVar, ref.id) 408 v := makeVar(ppVar) 409 s, err := v.AsString() 410 v.Release() 411 return s, err 412 } 413 414 // Rename renames a file or directory. 415 // 416 // Arguments file_ref and new_file_ref must both refer to files in the same file 417 // system. It is an error to rename a file or directory that is in use. It is 418 // not valid to rename a file in the external file system. 419 func (ref FileRef) Rename(newName FileRef) error { 420 code := ppb_fileref_rename(ref.id, newName.id, ppNullCompletionCallback) 421 return decodeError(Error(code)) 422 } 423 424 // MakeDirectory makes a new directory in the file system according to the given 425 // flags, which is a bit-mask of the MakeDirectoryFlag values. 426 // 427 // It is not valid to make a directory in the external file system. 428 func (ref FileRef) MakeDirectory(flags MakeDirectoryFlag) error { 429 code := ppb_fileref_make_directory(ref.id, int32(flags), ppNullCompletionCallback) 430 return decodeError(Error(code)) 431 } 432 433 // Stat queries info about a file or directory. 434 // 435 // You must have access to read this file or directory if it exists in the 436 // external filesystem. 437 func (ref FileRef) Stat() (info FileInfo, err error) { 438 var name string 439 name, err = ref.Name() 440 if err != nil { 441 return 442 } 443 var ppInfo pp_FileInfo 444 code := ppb_fileref_query(ref.id, &ppInfo, ppNullCompletionCallback) 445 if code < 0 { 446 err = decodeError(Error(code)) 447 return 448 } 449 info.fromPP(name, ppInfo) 450 return 451 } 452 453 // Touch Updates time stamps for a file. 454 // 455 // You must have write access to the file if it exists in the external filesystem. 456 func (ref FileRef) Touch(atime, mtime time.Time) error { 457 code := ppb_fileref_touch(ref.id, toPPTime(atime), toPPTime(mtime), ppNullCompletionCallback) 458 return decodeError(Error(code)) 459 } 460 461 // ReadDirectoryEntries reads all entries in a directory. 462 func (ref FileRef) ReadDirectoryEntries() (entries []DirectoryEntry, err error) { 463 var aout pp_ArrayOutput 464 var alloc arrayOutputBuffer 465 init_array_output(&aout, &alloc) 466 code := ppb_fileref_read_directory_entries(ref.id, aout, ppNullCompletionCallback) 467 if code < 0 { 468 err = decodeError(Error(code)) 469 return 470 } 471 472 count := alloc.count 473 for i := uint32(0); i < count; i++ { 474 ppEntry := (*pp_DirectoryEntry)(unsafe.Pointer(&alloc.buffer[i*alloc.size])) 475 var entry DirectoryEntry 476 entry.fromPP(*ppEntry) 477 entries = append(entries, entry) 478 } 479 return 480 } 481 482 // FileIO is used to operate on regular files. 483 type FileIO struct { 484 Resource 485 name string 486 position int64 487 } 488 489 // Open opens the specified regular file for I/O according to the given open 490 // flags, which is a bit-mask of the PP_FileOpenFlags values. 491 // 492 // Upon success, the corresponding file is classified as "in use" by this FileIO 493 // object until such time as the FileIO object is closed or destroyed. 494 func (file *FileIO) Open(ref FileRef, openFlags FileOpenFlag) error { 495 name, err := ref.Name() 496 if err != nil { 497 return err 498 } 499 code := ppb_fileio_open(file.id, ref.id, int32(openFlags), ppNullCompletionCallback) 500 if code >= 0 { 501 file.name = name 502 } 503 if openFlags&PP_FILEOPENFLAG_APPEND != 0 { 504 info, err := file.Stat() 505 if err != nil { 506 return err 507 } 508 file.position = info.Len 509 } 510 return decodeError(Error(code)) 511 } 512 513 // Close cancels any IO that may be pending, and closes the FileIO object. 514 // 515 // Any pending callbacks will still run, reporting PP_ERROR_ABORTED if pending 516 // IO was interrupted. It is not valid to call Open() again after a call to this 517 // method. Note: If the FileIO object is destroyed, and it is still open, then 518 // it will be implicitly closed, so you are not required to call Close(). 519 func (file *FileIO) Close() error { 520 ppb_fileio_close(file.id) 521 return nil 522 } 523 524 // Sync flushes changes to disk. 525 // 526 // This call can be very expensive! The FileIO object must have been opened with 527 // write access and there must be no other operations pending. 528 func (file *FileIO) Sync() error { 529 code := ppb_fileio_flush(file.id, ppNullCompletionCallback) 530 return decodeError(Error(code)) 531 } 532 533 // Stat queries info about the file opened by this FileIO object. 534 // 535 // The FileIO object must be opened, and there must be no other operations 536 // pending. 537 func (file *FileIO) Stat() (info FileInfo, err error) { 538 var ppInfo pp_FileInfo 539 code := ppb_fileio_query(file.id, &ppInfo, ppNullCompletionCallback) 540 if code < 0 { 541 err = decodeError(Error(code)) 542 return 543 } 544 info.fromPP(file.name, ppInfo) 545 return 546 } 547 548 // Read reads up to len(b) bytes from the File. It returns the number of bytes 549 // read and an error, if any. EOF is signaled by a zero count with err set to 550 // io.EOF. 551 func (file *FileIO) Read(buf []byte) (n int, err error) { 552 amount, err := file.ReadAt(buf, file.position) 553 file.position += int64(amount) 554 return amount, err 555 } 556 557 // Write writes len(b) bytes to the File. It returns the number of bytes written 558 // and an error, if any. Write returns a non-nil error when n != len(b). 559 func (file *FileIO) Write(buf []byte) (n int, err error) { 560 amount, err := file.WriteAt(buf, file.position) 561 file.position += int64(amount) 562 return amount, err 563 } 564 565 // WriteString is like Write, but writes the contents of string s rather than a 566 // slice of bytes. 567 func (file *FileIO) WriteString(s string) (n int, err error) { 568 return file.Write([]byte(s)) 569 } 570 571 // ReadAtOffset reads from an offset in the file. Does not affect the current 572 // file position. 573 // 574 // The size of the buf must be large enough to hold the specified number of 575 // bytes to read. This function might perform a partial read, meaning all the 576 // requested bytes might not be returned, even if the end of the file has not 577 // been reached. The FileIO object must have been opened with read access. 578 func (file *FileIO) ReadAt(buf []byte, off int64) (amount int, err error) { 579 code := ppb_fileio_read(file.id, off, &buf[0], int32(len(buf)), ppNullCompletionCallback) 580 if code < 0 { 581 err = decodeError(Error(code)) 582 return 583 } 584 if code == 0 { 585 err = io.EOF 586 return 587 } 588 amount = int(code) 589 return 590 } 591 592 // WriteAt writes to an offset in the file. Does not affect the current 593 // file position. 594 // 595 // This function might perform a partial write. The FileIO object must have been 596 // opened with write access. 597 func (file *FileIO) WriteAt(buf []byte, off int64) (amount int, err error) { 598 code := ppb_fileio_write(file.id, off, &buf[0], int32(len(buf)), ppNullCompletionCallback) 599 if code < 0 { 600 err = decodeError(Error(code)) 601 return 602 } 603 if code == 0 { 604 err = io.EOF 605 return 606 } 607 amount = int(code) 608 return 609 } 610 611 // Seek sets the offset for the next Read or Write on file to offset, 612 // interpreted according to whence: 0 means relative to the origin of the file, 613 // 1 means relative to the current offset, and 2 means relative to the end. It 614 // returns the new offset and an error, if any. 615 func (file *FileIO) Seek(offset int64, whence int) (ret int64, err error) { 616 switch whence { 617 case 0: 618 file.position = offset 619 case 1: 620 if offset > file.position { 621 err = errNegativeFilePosition 622 return 623 } 624 file.position += offset 625 case 2: 626 var info FileInfo 627 info, err = file.Stat() 628 if err != nil { 629 return 630 } 631 p := info.Len + offset 632 if p < 0 { 633 err = errNegativeFilePosition 634 return 635 } 636 file.position = p 637 } 638 ret = file.position 639 return 640 } 641 642 // Truncate sets the length of the file. 643 // 644 // If the file size is extended, then the extended area of the file is 645 // zero-filled. The FileIO object must have been opened with write access and 646 // there must be no other operations pending. 647 func (file *FileIO) Truncate(length int64) error { 648 code := ppb_fileio_set_length(file.id, length, ppNullCompletionCallback) 649 return decodeError(Error(code)) 650 } 651 652 // Touch Updates time stamps for the file opened by this FileIO object. 653 // 654 // This function will fail if the FileIO object has not been opened. The FileIO 655 // object must be opened, and there must be no other operations pending. 656 func (file *FileIO) Touch(atime, mtime time.Time) error { 657 code := ppb_fileio_touch(file.id, toPPTime(atime), toPPTime(mtime), ppNullCompletionCallback) 658 return decodeError(Error(code)) 659 } 660 661 // CreateFileSystem creates a file system with the given type. 662 func (inst Instance) CreateFileSystem(ty FileSystemType) (fs FileSystem, err error) { 663 id := ppb_filesystem_create(inst.id, ty) 664 if id == 0 { 665 err = errFileSystemCreateFailed 666 return 667 } 668 fs.instance = inst 669 fs.id = id 670 return 671 } 672 673 // CreateFileIO creates a new FileIO object. 674 func (inst Instance) CreateFileIO() (file FileIO, err error) { 675 id := ppb_fileio_create(inst.id) 676 if id == 0 { 677 err = errFileIOCreateFailed 678 return 679 } 680 file.id = id 681 return 682 }