This file is indexed.

/usr/share/tcltk/vfs1.3/template/quotavfs.tcl is in tcl-vfs 1.3-20080503-4.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
if 0 {
########################

quotavfs.tcl --

Written by Stephen Huntley (stephen.huntley@alum.mit.edu)
License: Tcl license
Version 1.5.2

A quota-enforcing virtual filesystem.  Requires the template vfs in templatevfs.tcl.

Quotas can be set on any quantity returned by "file stat" or "file attributes",
plus the attribute "filename", which is the fully normalized pathname of the file.

Two types of quota can be set: an incremented count of files matching a certain criterion, and
a running total of a certain quantity.  Each quota is defined by a set of switches composing 
a "quota group," any number of quota groups can be defined.  A file must fit within all quotas defined
to avoid triggering quota enforcement.

The quotas are enforced as a FIFO stack of files; that is, if a new file is copied to the vfs whose
attributes exceed a quota, the file is not rejected, rather, the already present files with 
the oldest access times that contribute to the quota are deleted until there is room within 
the quota limit for the addition of the new file.

The exception for the running total variety is if the file's attribute is large enough to 
exceed the quota by itself, it is barred without first deleting all other files contributing to 
the quota.

At mount time, all files in the existing directory are examined and quotas calculated.  Files may be
deleted to keep quotas under their defined limits.  After mount, when a new file is moved into the 
virtual directory or an existing file edited, its properties are examined with respect to the defined 
quotas; if no room can be made for it, the move or edit is rejected.

Usage: mount <quota group> ?<quota group>... ? <existing directory> <virtual directory>

Quota group definition:

-<quantity> <rule> -[quota|ruletotal] <quota number>
or
-<quantity> -total <quota number>

Options:

-<quantity>
Where <quantity> is any item returned by the "file stat" or "file attributes" commands, with the dash
prepended as needed, for example: -archive, -permissions, -size, -mtime etc.  The attribute "filename"
is assumed to exist as well, defined as the file's full pathname.  The quantity need not exist, so the 
same command line could be used on Unix or Windows, for example.  Nonexistent quantities have no effect
and are ignored.

<rule>
The rule is the criterion a file must meet to have the quota applied to it.  It may take the form of a 
list of glob patterns as used by the "string match" command: if the quantity value matches all the 
patterns, the quota is applied.  The rule may be Tcl code, to which the quantity value will be 
appended and then evaluated.  The code should return 1 if the file is judged to meet the 
quota criterion, or 0 if not.  If glob patterns are used, each pattern in the list may, in 
addition to symbols used by "string match", have a "!" prepended to it, which will negate the 
sense of the match.

-quota
If the quota group contains this switch, then the vfs will keep a running count of all files that satisfy 
the quota group's rule.  It will not allow more than the number of files specified in <quota number> to 
exist in the virtual file space.

-total
If the quota group contains this switch, then the vfs will track the sum of the values of the specified
quantity of all files.  It will not allow the sum specified in <quota number> to 
be exceeded in the virtual file space.

-ruletotal
Like -total, but a rule is defined, and only files satisfying the rule have their values added to the quota sum.

The quota vfs inherits the -cache and -volume options of the template vfs.


Examples -- to set a 10 MB size limit on your ftp upload directory:
mount -size -total 10000000 C:/temp/upload C:/vfs/ftp/pub

To allow only PNG or JPEG files in a photo collection:
mount -filename {!*.png !*.jpg !*.jpeg} -quota 0 /home/shuntley/photos /vfs/photo

To ban GIF files from your web site images subdirectory:
mount -filename {C:/Program Files/Apache/htdocs/images/*.gif} -quota 0 {C:/Program Files/Apache/htdocs} /docroot

To disallow creation of subdirectories:
mount -type directory -quota 0 /ftp/upload /intake

Use a rule to allow only 1 MB of files greater than 10kB in size:
mount -size {expr 10000 <} -ruletotal 1000000 /tmp /vfs/dump

Use two quota groups to allow only log files and keep only 1 more than one week:
mount -filename !*.log -quota 0 -mtime {expr [clock scan {7 days ago}] >} -quota 1 /var/log /vfs/history

########################
}

package require vfs::template 1.5

package require fileutil::globfind

namespace eval ::vfs::template::quota {

# read template procedures into current namespace. Do not edit:
foreach templateProc [namespace eval ::vfs::template {info procs}] {
	set infoArgs [info args ::vfs::template::$templateProc]
	set infoBody [info body ::vfs::template::$templateProc]
	proc $templateProc $infoArgs $infoBody
}

# edit following procedures:
proc close_ {channel} {
	upvar path path root root relative relative
	fconfigure $channel -translation binary
	seek $channel 0 end
	set quotaSize [tell $channel]
	seek $channel 0
	set filechannel [lindex $::vfs::template::quota::channels($channel) 0]
	set newFile [lindex $::vfs::template::quota::channels($channel) 1]
	unset ::vfs::template::quota::channels($channel)
	set file [file join $path $relative]

# Check if edited size violates any size quotas before allowing commit:
	if [catch {QuotaAdd $file}] {
		close $filechannel
		if $newFile {catch {file delete -force $file}}
		error "Disk quota exceeded"
	}
	seek $filechannel 0
	fcopy $channel $filechannel
	close $filechannel
	return
}
proc file_atime {file time} {
	upvar root root
	file atime $file $time
	append ::vfs::template::quota::atimes($root) " $time [list $file]"
	if {$::vfs::template::quota::files($file) < $time} {set ::vfs::template::quota::files($file) $time ; return}
	set ::vfs::template::quota::files($file) $time
	set aList {}
	foreach {atime afile} $::vfs::template::quota::atimes($root) {
		lappend aList "$atime [list $afile]"
	}
	set atimes {}
	foreach aset [lsort -dictionary $aList] {
		set atime [lindex $aset 0]
		set afile [lindex $aset 1]
		append atimes " $atime [list $afile]"
	}
	set ::vfs::template::quota::atimes($root) $atimes
}
proc file_mtime {file time} {file mtime $file $time}
proc file_attributes {file {attribute {}} args} {eval file attributes \$file $attribute $args}
proc file_delete {file} {
	upvar root root
	array set quotaArray $::vfs::template::quota::quota($root)
	QuotaDelete $file
	set ::vfs::template::quota::quota($root) [array get quotaArray]
	return
}
proc file_executable {file} {file executable $file}
proc file_exists {file} {file exists $file}
proc file_mkdir {file} {
	upvar root root
	file mkdir $file
	globfind $file QuotaAdd
	return
}
proc file_readable {file} {file readable $file}
proc file_stat {file array} {upvar $array fs ; ::file stat $file fs}
proc file_writable {file} {file writable $file}
proc glob_ {directory dir nocomplain tails types typeString dashes pattern} {glob -directory $dir -nocomplain -tails -types $typeString -- $pattern}
proc open_ {file mode} {
	upvar root root permissions permissions
	upvar newFile newFile
	if {$mode == "r"} {
		set atime [clock seconds]
		append ::vfs::template::quota::atimes($root) " $atime [list $file]"
		set ::vfs::template::quota::files($file) $atime
		return [open $file r]
	}

if $newFile {
	set now [clock seconds]
	set fstat "mtime $now atime $now mode $permissions type file ctime $now size 0"
	QuotaAdd $file
}
	set channel [open $file $mode]

# Check if new file violates any quotas by adding it to quota tallies:
#	if $newFile {
#		set err [catch {QuotaAdd $file} result]
#		if $err {
#			close $channel
#			file delete -force -- $file
#			vfs::filesystem posixerror $::vfs::posix(EDQUOT)
#			error "Disk quota exceeded"
#		}
#	}
# remove file from quota tallies until channel is closed:
	array set quotaArray $::vfs::template::quota::quota($root)
	QuotaDelete $file 0
	set ::vfs::template::quota::quota($root) [array get quotaArray]

# Use memchan to store edits so edit can be rejected if it violates size quotas:
	set memchannel [memchan]
	fconfigure $channel -translation binary
	fconfigure $memchannel -translation binary
	seek $channel 0
	fcopy $channel $memchannel
	set [namespace current]::channels($memchannel) "$channel $newFile"
	return $memchannel
}

proc MountProcedure {args} {
	upvar volume volume

# take real and virtual directories from command line args.
	set to [lindex $args end]
	if [string equal $volume {}] {set to [::file normalize $to]}
	set path [::file normalize [lindex $args end-1]]

# make sure mount location exists:
	::file mkdir $path

# add custom handling for new vfs args here.

	namespace import -force ::fileutil::globfind::globfind
	set quotaArgs [lrange $args 0 end-2]

	ParseArgs ::vfs::template::quota::quota($to) $quotaArgs

# Initialize quotas:
	set root $to
	set aList {}
	foreach afile [globfind $path] {
		file stat $afile fs
		lappend aList "$fs(atime) [list $afile]"
	}
	set atimes {}
	foreach aset [lsort -dictionary $aList] {
		set atime [lindex $aset 0]
		set afile [lindex $aset 1]
		append atimes " $atime [list $afile]"
		set ::vfs::template::quota::files($afile) $atime
	}
	set ::vfs::template::quota::atimes($root) $atimes

	globfind $path QuotaAdd

	set ::vfs::template::quota::atimes($root) $atimes

# return two-item list consisting of real and virtual locations.
	lappend pathto $path
	lappend pathto $to
	return $pathto
}


proc UnmountProcedure {path to} {
# add custom unmount handling of new vfs elements here.

	unset -nocomplain ::vfs::template::quota::quota($to)
	unset -nocomplain ::vfs::template::quota::atimes($to)
	return
}

# Default rule for quotas with pattern specified:
proc CheckPattern {pattern value} {
	foreach ptn $pattern {
		set negate [string equal [string index $ptn 0] !]
		if $negate {set ptn [string range $ptn 1 end]}
		set match [string match $ptn $value]
		if $negate {set match [expr !$match]}
		if !$match {return 0}
	}
	return 1
}

# Used as argument to proc globfind to recurse down dir hierarchies and process each file and dir found:
proc QuotaAdd {fileName} {
	set caller [lindex [info level -1] 0]
	if {$caller == "MountProcedure"} {set init 1} else {set init 0}
	upvar path path root root quotaSize quotaSize fstat fstat
	if ![string first ".vfs_" [file tail $fileName]] {return 0}
	if {[info exists path] && ($fileName == $path)} {return 0}
	array set quotaArray $::vfs::template::quota::quota($root)
	set overLimit {}
	set items [lsort -unique [string map {",type " " " ",rule " " " ",quota " " " ",current " " "} " [array names quotaArray] "]]

	set delete 1
if [info exists fstat] {
	array set fs $fstat	
} else {
	set noexist [catch {file stat $fileName fs}]
	if $noexist {return 0}
}
	set fs(filename) $fileName

# if this call is being used to check edits, replace file size with channel size and don't delete file if edit too big:
	if [info exists quotaSize] {set fs(size) $quotaSize ; set delete 0 ; unset quotaSize}

# Update queue which tracks which files to try deleting first to make room for new files:
	append ::vfs::template::quota::atimes($root) " $fs(atime) [list $fileName]"
	set ::vfs::template::quota::files($fileName) $fs(atime)

# Check each defined quota to see if given file violates it:
	foreach item $items {
		regexp {([0-9]*),(.*)} $item trash groupCount item
		if ![info exists fs($item)] {if [file exists $fileName] {array set fs [file attributes $fileName]}}
		if ![info exists fs($item)] {continue}
		set contrib [eval $quotaArray($groupCount,$item,rule) [list $fs($item)]]
		if $contrib	{	
			if {$quotaArray($groupCount,$item,type) == "total"} {

				# If file quantity by itself would violate quota, reject immediately:
				if {$fs($item) > $quotaArray($groupCount,$item,quota)} {
					if $delete {catch {file delete -force -- $fileName} result}
if [info exists ::vfs::template::quota::debug] {
puts "\n$fileName violates quota by itself:
$item: $fs($item)
quota: $quotaArray($groupCount,$item,quota)"
if $delete {puts "$fileName deleted: $result"}
}
					if $init {return 0} else {vfs::filesystem posixerror $::vfs::posix(EDQUOT)}
				}
				set quotaArray($groupCount,$item,current) [expr $quotaArray($groupCount,$item,current) + $fs($item)]
			} else {
				if {$quotaArray($groupCount,$item,quota) == 0} {
					if $delete {catch {file delete -force -- $fileName} result}
if [info exists ::vfs::template::quota::debug] {
puts "\n$fileName violates quota by itself:
$item: $fs($item)
quota: $quotaArray($groupCount,$item,quota)"
if $delete {puts "$fileName deleted: $result"}
}
					if $init {return 0} else {vfs::filesystem posixerror $::vfs::posix(EDQUOT)}
				}
				incr quotaArray($groupCount,$item,current)
			}
			# If file violates quota, store quota to see if room can be made by deleting older files:
			if {$quotaArray($groupCount,$item,current) > $quotaArray($groupCount,$item,quota)} {lappend overLimit "$groupCount,$item"}
		}
	}
# if given file puts some quotas over limit, see if room can be made by deleting older files:

	foreach item $overLimit {
		set itm [lindex [split $item ,] 1]
		if {$quotaArray($item,current) <= $quotaArray($item,quota)} {continue}

		# examine queue of stored atimes to find older files:
		foreach {atime afile} $::vfs::template::quota::atimes($root) {

			# If stored atime doesn't match latest value, delete record and move on:
			if {($::vfs::template::quota::files($afile) != $atime) || ![file exists $afile]} {
				set deleteLoc [lsearch -exact $::vfs::template::quota::atimes($root) $afile]
				set ::vfs::template::quota::atimes($root) [lreplace $::vfs::template::quota::atimes($root) [expr $deleteLoc - 1] $deleteLoc]
				if {[lsearch -exact $::vfs::template::quota::atimes($root) $afile] < 0} {unset ::vfs::template::quota::files($afile)}
				continue
			}
			
			# if file from queue is in fact newer than given file, skip it:
			if {$atime > $fs(atime)} {continue}

			# if stored filename is same as given filename, given filename violates quota and must be rejected:
			if {$afile == $fileName} {
				if !$delete {set quotaSize $fs(size)}
				catch {QuotaDelete $fileName $delete}
				set ::vfs::template::quota::quota($root) [array get quotaArray]
				if $init {return 0} else {vfs::filesystem posixerror $::vfs::posix(EDQUOT)}
			}

			# If stored file contributes to quota, delete it and remove from quota tally:

			if {$itm == "filename"} {
				set itm_val $afile
			} elseif {[string index $itm 0] == "-"} {
				set itm_val [file attributes $afile $itm]
			} else {
				file stat $afile iv
				set itm_val $iv($itm)
			}
	
			set contrib [eval $quotaArray($item,rule) [list $itm_val]]
			if $contrib	{	
				if {$quotaArray($item,type) == "total"} {
					set itm [lindex [split $item ,] 1]
					if {[string index $itm 0] == "-"} {
						set itm_val [file attributes $afile $itm]
					} else {
						file stat $afile iv
						set itm_val $iv($itm)
					}
					if !$itm_val {continue}
				}
				set ::vfs::template::quota::quota($root) [array get quotaArray]
				QuotaDelete $afile
			}

			# If deletions make room for new file, then OK:
			if {$quotaArray($item,current) <= $quotaArray($item,quota)} {break}
		}
	}
	set ::vfs::template::quota::quota($root) [array get quotaArray]
	return 0
}

proc QuotaDelete {fileName {delete 1}} {
	upvar quotaArray quotaArray quotaSize quotaSize
	set items [lsort -unique [string map {",type " " " ",rule " " " ",quota " " " ",current " " "} " [array names quotaArray] "]]

# If given fileName is a dir, must remove all contents from quota tallies before removing dir itself:
	set files [lsort -decreasing [globfind $fileName]]
	set type file

# Must parse contents twice, eliminate files first, then dirs:
	foreach file [concat $files //// $files] {
		if {$file == "////"} {set type directory ; continue}
	
		# cache quantity info to save time on second pass:
		if ![info exists stat($file)] {
			file stat $file fs
			set fs(filename) $fileName
			if [info exists quotaSize] {set fs(size) $quotaSize}
			set stat($file) [array get fs]
		}
		array set fs $stat($file)

		# If file type is wrong for this pass, continue:
		if {($type == "file") && ($fs(type) == "directory")} {continue}
		if {($type == "directory") && ($fs(type) != "directory")} {continue}

		# Check each quota to see if current file contributes to it:
		foreach item $items {
		 	regexp {([0-9]*),(.*)} $item trash groupCount item
			if ![info exists fs($item)] {if [file exists $file] {array set fs [file attributes $file]} ; set stat($file) [array get fs]}
			if ![info exists fs($item)] {continue}
			set contrib [eval $quotaArray($groupCount,$item,rule) [list $fs($item)]]
			if $contrib	{
				if {$quotaArray($groupCount,$item,type) == "total"} {
					set quotaArray($groupCount,$item,current) [expr $quotaArray($groupCount,$item,current) - $fs($item)]
				} else {
					incr quotaArray($groupCount,$item,current) -1
				}
if [info exists ::vfs::template::quota::debug] {
puts "\n$file contributed to quota:
rule: $quotaArray($groupCount,$item,rule)
quota: $quotaArray($groupCount,$item,quota)
current: $quotaArray($groupCount,$item,current)"
}
			}
		}

		# After removing file from quota tallies, delete it:
		if $delete {file delete -force -- $file}
if {$delete && [info exists ::vfs::template::quota::debug]} {
puts "\n$file deleted"
}
	}
	return
}

# Decided on new command line syntax, rather than rewrite whole vfs,
# this proc casts new syntax into old format, then processes as before:
proc ParseArgs {argsStore args} {
	upvar path path
	set args [lindex $args 0]

	array set attrs [file attributes $path]
	set quotas {}
	set totals {}
	set rtotals {}
	set newArgs {}

# find location of each quota group:
	set qPosition [lsearch -all $args "-quota"]
	set tPosition [lsearch -all $args "-total"]
	set rPosition [lsearch -all $args "-ruletotal"]

# break group defs into separate categories:
	foreach qp $qPosition {
		incr qp
		append quotas " [lrange $args [expr $qp - 3] $qp]" 
	}

	foreach tp $tPosition {
		incr tp
		append totals " [lrange $args [expr $tp - 2] $tp]" 
	}

	foreach rp $rPosition {
		incr rp
		append rtotals " [lrange $args [expr $rp - 3] $rp]" 
	}

# cast each category into old syntax:
	foreach {type pr quota number} $quotas {
		set patrul "-pattern"
		if {[lsearch -exact [info commands [string trim [string range $pr 0 [string first { } $pr]]]] [string trim [string range $pr 0 [string first { } $pr]]]] > -1} {
			set patrul "-rule"
		}
		if ![info exists attrs($type)] {set type [string range $type 1 end]}
		append newArgs " -number: -item $type $patrul [list $pr] -quota $number"
	}

	foreach {type total number} $totals {
		if ![info exists attrs($type)] {set type [string range $type 1 end]}
		append newArgs " -total: -item $type -quota $number"
	}

	foreach {type pr rtotal number} $rtotals {
		set patrul "-pattern"
		if {[lsearch -exact [info commands [string trim [string range $pr 0 [string first { } $pr]]]] [string trim [string range $pr 0 [string first { } $pr]]]] > -1} {
			set patrul "-rule"
		}
		if ![info exists attrs($type)] {set type [string range $type 1 end]}
		append newArgs " -total: -item $type $patrul [list $pr] -quota $number"
	}

# process old syntax:
	unset args
	lappend args [string trim $newArgs]

	set groupCount 0
	set args [lindex $args 0]
	set argsIndex [llength $args]
	for {set i $argsIndex} {$i >= 0} {incr i -1} {
		switch -- [lindex $args $i] {
			-number: -
			-total: {
				set item $itemSet(item)
				if ![info exists itemSet(rule)] {set itemSet(rule) "CheckPattern *"}
				set argsArray($groupCount,$item,type) [string range [lindex $args $i] 1 end-1]
				set argsArray($groupCount,$item,rule) $itemSet(rule)
				set argsArray($groupCount,$item,quota) $itemSet(quota)
				set argsArray($groupCount,$item,current) 0
				array unset itemSet
				incr groupCount
			}
			-item {
				set itemSet(item) [lindex $args [expr $i + 1]]
			}
			-pattern {
				set itemSet(rule) "CheckPattern [list [lindex $args [expr $i + 1]]]"
			}
			-quota {
				set itemSet(quota) [lindex $args [expr $i + 1]]
			}
			-rule {
				set itemSet(rule) [lindex $args [expr $i + 1]]
			}
		}
	}
	set $argsStore [array get argsArray]
}

}
# end namespace ::vfs::template::quota