Class: File::Stat

Inherits:
Object
  • Object
show all
Includes:
Comparable, Windows::Stat::Constants, Windows::Stat::Functions, Windows::Stat::Structs
Defined in:
lib/win32/file/stat.rb

Constant Summary collapse

WIN32_FILE_STAT_VERSION =

The version of the win32-file-stat library

'1.5.5'

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Windows::Stat::Functions

attach_pfunc

Constructor Details

#initialize(file) ⇒ Stat

Creates and returns a File::Stat object, which encapsulate common status information for File objects on MS Windows sytems. The information is recorded at the moment the File::Stat object is created; changes made to the file after that point will not be reflected.



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/win32/file/stat.rb', line 68

def initialize(file)
  file = string_check(file)

  path  = file.tr('/', "\\")
  @path = path

  @user_sid = get_file_sid(file, OWNER_SECURITY_INFORMATION)
  @grp_sid  = get_file_sid(file, GROUP_SECURITY_INFORMATION)

  @uid = @user_sid.split('-').last.to_i
  @gid = @grp_sid.split('-').last.to_i

  @owned = @user_sid == get_current_process_sid(TokenUser)
  @grpowned = @grp_sid == get_current_process_sid(TokenGroups)

  begin
    # The handle returned will be used by other functions
    handle = get_handle(path)

    @blockdev = get_blockdev(path)
    @blksize  = get_blksize(path)

    if handle
      @filetype = get_filetype(handle)
      @streams  = get_streams(handle)
      @chardev  = @filetype == FILE_TYPE_CHAR
      @regular  = @filetype == FILE_TYPE_DISK
      @pipe     = @filetype == FILE_TYPE_PIPE

      if @pipe
        @socket = !GetNamedPipeInfo(handle, nil, nil, nil, nil)
      else
        @socket = false
      end
    else
      @chardev = false
      @regular = false
      @pipe    = false
      @socket  = false
    end

    fpath = path.wincode

    if handle == nil || ((@blockdev || @chardev || @pipe) && GetDriveType(fpath) != DRIVE_REMOVABLE)
      data = WIN32_FIND_DATA.new
      CloseHandle(handle) if handle

      handle = FindFirstFile(fpath, data)

      if handle == INVALID_HANDLE_VALUE
        raise SystemCallError.new('FindFirstFile', FFI.errno)
      end

      FindClose(handle)
      handle = nil

      @nlink = 1 # Default from stat/wstat function.
      @ino   = nil
      @rdev  = nil
    else
      data = BY_HANDLE_FILE_INFORMATION.new

      unless GetFileInformationByHandle(handle, data)
        raise SystemCallError.new('GetFileInformationByHandle', FFI.errno)
      end

      @nlink = data[:nNumberOfLinks]
      @ino   = (data[:nFileIndexHigh] << 32) | data[:nFileIndexLow]
      @rdev  = data[:dwVolumeSerialNumber]
    end

    # Not supported and/or meaningless on MS Windows
    @dev_major      = nil
    @dev_minor      = nil
    @rdev_major     = nil
    @rdev_minor     = nil
    @setgid         = false
    @setuid         = false
    @sticky         = false

    # Originally used GetBinaryType, but it only worked
    # for .exe files, and it could return false positives.
    @executable = %w[.bat .cmd .com .exe].include?(File.extname(@path).downcase)

    # Set blocks equal to size / blksize, rounded up
    case @blksize
      when nil
        @blocks = nil
      when 0
        @blocks = 0
      else
        @blocks  = (data.size.to_f / @blksize.to_f).ceil
    end

    @attr  = data[:dwFileAttributes]
    @atime = Time.at(data.atime)
    @ctime = Time.at(data.ctime)
    @mtime = Time.at(data.mtime)
    @size  = data.size

    @archive       = @attr & FILE_ATTRIBUTE_ARCHIVE > 0
    @compressed    = @attr & FILE_ATTRIBUTE_COMPRESSED > 0
    @directory     = @attr & FILE_ATTRIBUTE_DIRECTORY > 0
    @encrypted     = @attr & FILE_ATTRIBUTE_ENCRYPTED > 0
    @hidden        = @attr & FILE_ATTRIBUTE_HIDDEN > 0
    @indexed       = @attr & ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED > 0
    @normal        = @attr & FILE_ATTRIBUTE_NORMAL > 0
    @offline       = @attr & FILE_ATTRIBUTE_OFFLINE > 0
    @readonly      = @attr & FILE_ATTRIBUTE_READONLY > 0
    @reparse_point = @attr & FILE_ATTRIBUTE_REPARSE_POINT > 0
    @sparse        = @attr & FILE_ATTRIBUTE_SPARSE_FILE > 0
    @system        = @attr & FILE_ATTRIBUTE_SYSTEM > 0
    @temporary     = @attr & FILE_ATTRIBUTE_TEMPORARY > 0

    @mode = get_mode

    @readable = access_check(path, GENERIC_READ)
    @readable_real = @readable

    # The MSDN docs say that the readonly attribute is honored for directories
    if @directory
      @writable = access_check(path, GENERIC_WRITE)
    else
      @writable = access_check(path, GENERIC_WRITE) && !@readonly
    end

    @writable_real = @writable

    @world_readable = access_check_world(path, FILE_READ_DATA)
    @world_writable = access_check_world(path, FILE_WRITE_DATA) && !@readonly

    if @reparse_point
      @symlink = get_symlink(path)
    else
      @symlink = false
    end
  ensure
    CloseHandle(handle) if handle
  end
end

Instance Attribute Details

#atimeObject (readonly)

A Time object containing the last access time.



25
26
27
# File 'lib/win32/file/stat.rb', line 25

def atime
  @atime
end

#blksizeObject (readonly)

The native filesystems’ block size.



34
35
36
# File 'lib/win32/file/stat.rb', line 34

def blksize
  @blksize
end

#blocksObject (readonly)

The number of native filesystem blocks allocated for this file.



37
38
39
# File 'lib/win32/file/stat.rb', line 37

def blocks
  @blocks
end

#ctimeObject (readonly)

A Time object indicating when the file was last changed.



28
29
30
# File 'lib/win32/file/stat.rb', line 28

def ctime
  @ctime
end

#dev_majorObject (readonly)

Nil on Windows



55
56
57
# File 'lib/win32/file/stat.rb', line 55

def dev_major
  @dev_major
end

#dev_minorObject (readonly)

Nil on Windows



55
56
57
# File 'lib/win32/file/stat.rb', line 55

def dev_minor
  @dev_minor
end

#inoObject (readonly)

The file’s unique identifier. Only valid for regular files.



43
44
45
# File 'lib/win32/file/stat.rb', line 43

def ino
  @ino
end

#modeObject (readonly)

Integer representing the permission bits of the file.



46
47
48
# File 'lib/win32/file/stat.rb', line 46

def mode
  @mode
end

#mtimeObject (readonly)

A Time object containing the last modification time.



31
32
33
# File 'lib/win32/file/stat.rb', line 31

def mtime
  @mtime
end

The number of hard links to the file.



49
50
51
# File 'lib/win32/file/stat.rb', line 49

def nlink
  @nlink
end

#rdevObject (readonly)

The serial number of the file’s volume.



40
41
42
# File 'lib/win32/file/stat.rb', line 40

def rdev
  @rdev
end

#rdev_majorObject (readonly)

Nil on Windows



55
56
57
# File 'lib/win32/file/stat.rb', line 55

def rdev_major
  @rdev_major
end

#rdev_minorObject (readonly)

Nil on Windows



55
56
57
# File 'lib/win32/file/stat.rb', line 55

def rdev_minor
  @rdev_minor
end

#sizeObject (readonly)

The size of the file in bytes.



52
53
54
# File 'lib/win32/file/stat.rb', line 52

def size
  @size
end

#streamsObject (readonly)

Alternate streams



58
59
60
# File 'lib/win32/file/stat.rb', line 58

def streams
  @streams
end

Instance Method Details

#<=>(other) ⇒ Object

Compares two File::Stat objects using modification time. – Custom implementation necessary since we altered File::Stat.



215
216
217
# File 'lib/win32/file/stat.rb', line 215

def <=>(other)
  @mtime.to_i <=> other.mtime.to_i
end

#archive?Boolean

Returns whether or not the file is an archive file.



223
224
225
# File 'lib/win32/file/stat.rb', line 223

def archive?
  @archive
end

#blockdev?Boolean

Returns whether or not the file is a block device. For MS Windows a block device is a removable drive, cdrom or ramdisk.



230
231
232
# File 'lib/win32/file/stat.rb', line 230

def blockdev?
  @blockdev
end

#chardev?Boolean

Returns whether or not the file is a character device.



236
237
238
# File 'lib/win32/file/stat.rb', line 236

def chardev?
  @chardev
end

#compressed?Boolean

Returns whether or not the file is compressed.



242
243
244
# File 'lib/win32/file/stat.rb', line 242

def compressed?
  @compressed
end

#dev(letter = false) ⇒ Object

Returns the drive number of the disk containing the file, or -1 if there is no associated drive number.

If the letter option is true, returns the drive letter instead. If there is no drive letter, it will return nil. – This differs slightly from MRI in that it will return -1 if the path does not have a drive letter.

Note: Bug in JRuby as of JRuby 1.7.8, which does not expand NUL properly.



336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/win32/file/stat.rb', line 336

def dev(letter = false)
  fpath = File.expand_path(@path).wincode
  num = PathGetDriveNumber(fpath)

  if letter
    if num == -1
      nil
    else
      (num + 'A'.ord).chr + ':'
    end
  else
    num
  end
end

#directory?Boolean

Returns whether or not the file is a directory.



248
249
250
# File 'lib/win32/file/stat.rb', line 248

def directory?
  @directory
end

#encrypted?Boolean

Returns whether or not the file in encrypted.



254
255
256
# File 'lib/win32/file/stat.rb', line 254

def encrypted?
  @encrypted
end

#executable?Boolean Also known as: executable_real?

Returns whether or not the file is executable. Generally speaking, this means .bat, .cmd, .com, and .exe files.



261
262
263
# File 'lib/win32/file/stat.rb', line 261

def executable?
  @executable
end

#file?Boolean

Returns whether or not the file is a regular file, as opposed to a pipe, socket, etc.



270
271
272
# File 'lib/win32/file/stat.rb', line 270

def file?
  @regular && !@directory && !@reparse_point
end

#ftypeObject

Identifies the type of file. The return string is one of ‘file’, ‘directory’, ‘characterSpecial’, ‘socket’ or ‘unknown’.



498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
# File 'lib/win32/file/stat.rb', line 498

def ftype
  return 'directory' if @directory

  case @filetype
    when FILE_TYPE_CHAR
      'characterSpecial'
    when FILE_TYPE_DISK
      'file'
    when FILE_TYPE_PIPE
      'socket'
    else
      if blockdev?
        'blockSpecial'
      else
        'unknown'
      end
  end
end

#gid(full_sid = false) ⇒ Object

Returns the user ID of the file. If full_sid is true, then the full string sid is returned instead. – The user id is the RID of the SID.



279
280
281
# File 'lib/win32/file/stat.rb', line 279

def gid(full_sid = false)
  full_sid ? @grp_sid : @gid
end

#grpowned?Boolean

Returns true if the process owner’s ID is the same as one of the file’s groups. – Internally we’re checking the process sid against the TokenGroups sid.



287
288
289
# File 'lib/win32/file/stat.rb', line 287

def grpowned?
  @grpowned
end

#hidden?Boolean

Returns whether or not the file is hidden.



293
294
295
# File 'lib/win32/file/stat.rb', line 293

def hidden?
  @hidden
end

#indexed?Boolean Also known as: content_indexed?

Returns whether or not the file is content indexed.



299
300
301
# File 'lib/win32/file/stat.rb', line 299

def indexed?
  @indexed
end

#inspectObject

Returns a stringified version of a File::Stat object.



519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
# File 'lib/win32/file/stat.rb', line 519

def inspect
  members = %w[
    archive? atime blksize blockdev? blocks compressed? ctime dev
    encrypted? gid hidden? indexed? ino mode mtime rdev nlink normal?
    offline? readonly? reparse_point? size sparse? system? streams
    temporary? uid
  ]

  str = "#<#{self.class}"

  members.sort.each{ |mem|
    if mem == 'mode'
      str << " #{mem}=" << sprintf("0%o", send(mem.intern))
    elsif mem[-1].chr == '?' # boolean methods
      str << " #{mem.chop}=" << send(mem.intern).to_s
    else
      str << " #{mem}=" << send(mem.intern).to_s
    end
  }

  str
end

#normal?Boolean

Returns whether or not the file is ‘normal’. This is only true if virtually all other attributes are false.



308
309
310
# File 'lib/win32/file/stat.rb', line 308

def normal?
  @normal
end

#offline?Boolean

Returns whether or not the file is offline.



314
315
316
# File 'lib/win32/file/stat.rb', line 314

def offline?
  @offline
end

#owned?Boolean

Returns whether or not the current process owner is the owner of the file. – Internally we’re checking the process sid against the owner’s sid.



321
322
323
# File 'lib/win32/file/stat.rb', line 321

def owned?
  @owned
end

#pipe?Boolean

Returns whether or not the file is a pipe.



375
376
377
# File 'lib/win32/file/stat.rb', line 375

def pipe?
  @pipe
end

#pretty_print(q) ⇒ Object

A custom pretty print method. This was necessary not only to handle the additional attributes, but to work around an error caused by the builtin method for the current File::Stat class (see pp.rb).



546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
# File 'lib/win32/file/stat.rb', line 546

def pretty_print(q)
  members = %w[
    archive? atime blksize blockdev? blocks compressed? ctime dev
    encrypted? gid hidden? indexed? ino mode mtime rdev nlink normal?
    offline? readonly? reparse_point? size sparse? streams system? temporary?
    uid
  ]

  q.object_group(self){
    q.breakable
    members.each{ |mem|
      q.group{
        q.text("#{mem}".ljust(15) + "=> ")
        if mem == 'mode'
          q.text(sprintf("0%o", send(mem.intern)))
        else
          val = self.send(mem.intern)
          if val.nil?
            q.text('nil')
          else
            q.text(val.to_s)
          end
        end
      }
      q.comma_breakable unless mem == members.last
    }
  }
end

#readable?Boolean

Returns whether or not the file is readable by the process owner. – In Windows terms, we’re checking for GENERIC_READ privileges.



355
356
357
# File 'lib/win32/file/stat.rb', line 355

def readable?
  @readable
end

#readable_real?Boolean

A synonym for File::Stat#readable?



361
362
363
# File 'lib/win32/file/stat.rb', line 361

def readable_real?
  @readable_real
end

#readonly?Boolean Also known as: read_only?

Returns whether or not the file is readonly.



367
368
369
# File 'lib/win32/file/stat.rb', line 367

def readonly?
  @readonly
end

#reparse_point?Boolean

Returns whether or not the file is a reparse point.



386
387
388
# File 'lib/win32/file/stat.rb', line 386

def reparse_point?
  @reparse_point
end

#setgid?Boolean

Returns false on MS Windows. – I had to explicitly define this because of a bug in JRuby.



394
395
396
# File 'lib/win32/file/stat.rb', line 394

def setgid?
  @setgid
end

#setuid?Boolean

Returns false on MS Windows. – I had to explicitly define this because of a bug in JRuby.



402
403
404
# File 'lib/win32/file/stat.rb', line 402

def setuid?
  @setuid
end

#size?Boolean

Returns whether or not the file size is zero.



408
409
410
# File 'lib/win32/file/stat.rb', line 408

def size?
  @size > 0 ? @size : nil
end

#socket?Boolean

Returns whether or not the file is a socket.



380
381
382
# File 'lib/win32/file/stat.rb', line 380

def socket?
  @socket
end

#sparse?Boolean

Returns whether or not the file is a sparse file. In most cases a sparse file is an image file.



415
416
417
# File 'lib/win32/file/stat.rb', line 415

def sparse?
  @sparse
end

#sticky?Boolean

Returns false on MS Windows. – I had to explicitly define this because of a bug in JRuby.



423
424
425
# File 'lib/win32/file/stat.rb', line 423

def sticky?
  @sticky
end

#symlink?Boolean

Returns whether or not the file is a symlink.



429
430
431
# File 'lib/win32/file/stat.rb', line 429

def symlink?
  @symlink
end

#system?Boolean

Returns whether or not the file is a system file.



435
436
437
# File 'lib/win32/file/stat.rb', line 435

def system?
  @system
end

#temporary?Boolean

Returns whether or not the file is being used for temporary storage.



441
442
443
# File 'lib/win32/file/stat.rb', line 441

def temporary?
  @temporary
end

#uid(full_sid = false) ⇒ Object

Returns the user ID of the file. If the full_sid is true, then the full string sid is returned instead. – The user id is the RID of the SID.



450
451
452
# File 'lib/win32/file/stat.rb', line 450

def uid(full_sid = false)
  full_sid ? @user_sid : @uid
end

#world_readable?Boolean

Returns whether or not the file is readable by others. Note that this merely returns true or false, not permission bits (or nil). – In Windows terms, this is checking the access right FILE_READ_DATA against the well-known SID “S-1-1-0”, aka “Everyone”.



461
462
463
# File 'lib/win32/file/stat.rb', line 461

def world_readable?
  @world_readable
end

#world_writable?Boolean

Returns whether or not the file is writable by others. Note that this merely returns true or false, not permission bits (or nil). – In Windows terms, this is checking the access right FILE_WRITE_DATA against the well-known SID “S-1-1-0”, aka “Everyone”.



471
472
473
# File 'lib/win32/file/stat.rb', line 471

def world_writable?
  @world_writable
end

#writable?Boolean

Returns whether or not the file is writable by the current process owner. – In Windows terms, we’re checking for GENERIC_WRITE privileges.



479
480
481
# File 'lib/win32/file/stat.rb', line 479

def writable?
  @writable
end

#writable_real?Boolean

A synonym for File::Stat#readable?



485
486
487
# File 'lib/win32/file/stat.rb', line 485

def writable_real?
  @writable_real
end

#zero?Boolean

Returns whether or not the file size is zero.



491
492
493
# File 'lib/win32/file/stat.rb', line 491

def zero?
  @size == 0
end