-
-
Save mmdemirbas/5229315 to your computer and use it in GitHub Desktop.
######################################################################### | |
# # | |
# Script to set or clear read-only flag of an NTFS volume. # | |
# # | |
# Usage: .\set-ntfs-ro.ps1 set "MY DISK LABEL" # | |
# .\set-ntfs-ro.ps1 clear "MY DISK LABEL" # | |
# # | |
# Author: Muhammed Demirbas, mmdemirbas at gmail dot com # | |
# Date : 2013-03-23 # | |
# # | |
######################################################################### | |
param($setOrClear, $diskLabel) | |
if( [string]::IsNullOrWhiteSpace($setOrClear) ) | |
{ | |
$ScriptName = $MyInvocation.MyCommand.Name | |
"usage: .\$ScriptName set ""MY DISK LABEL""" | |
" .\$ScriptName clear ""MY DISK LABEL""" | |
return | |
} | |
if( $setOrClear -ne "set" -and $setOrClear -ne "clear" ) | |
{ | |
throw 'Valid actions are "set" and "clear"!' | |
} | |
if( [string]::IsNullOrWhiteSpace($diskLabel) ) | |
{ | |
throw "Please specify a non-blank disk label!" | |
} | |
# Path of the temporary file to use as diskpart script | |
$scriptFile = "$env:TMP\set-ntfs-ro-script.tmp" | |
# Save "list volume" command to a temp-file | |
"list volume" | Out-File -Encoding ascii $scriptFile | |
# Execute diskpart providing the script, and select the involved line | |
$matches = diskpart /s $scriptFile | Select-String $diskLabel | |
if( $matches.Length -eq 0 ) | |
{ | |
throw "No match for the label: $diskLabel" | |
} | |
elseif ( $matches.Length -ge 2 ) | |
{ | |
throw "More than one match for the label: $diskLabel" | |
} | |
# Obtain volume number | |
$words = $matches.Line.Trim().Split(" ") | |
if( !$words -or $words.Length -le 1 ) | |
{ | |
throw "Volume number couldn't be obtained for the volume:`n$line" | |
} | |
$volumeNum = $words.Get(1) | |
# Save the command to modify read-only flag to a temp-file | |
"select volume $volumeNum | |
att vol $setOrClear readonly | |
detail vol" | Out-File -Encoding ascii $scriptFile | |
# Execute the command, and print details | |
diskpart /s $scriptFile | |
# Clean the waste | |
del $scriptFile |
@DarthJahus try to format your drive as NTFS, since this flag "READONLY" is only available on NTFS.
Hi! Nice script! I've digged into it and surprisingly came to even simpler solution:
$scriptVolumeLabel = "Music"
$scriptDriveLetter = ( Get-Volume -FileSystemLabel $scriptVolumeLabel ).DriveLetter
if ([string]::IsNullOrEmpty($scriptDriveLetter))
{
echo "Volume '$scriptVolumeLabel' was not found!"
echo ""
pause
exit
}
Write-VolumeCache -DriveLetter $scriptDriveLetter
Set-Partition -DriveLetter $scriptDriveLetter -IsReadOnly $true
as you can see there is no need in "diskpart" now! Cool!
To enable write, just change two last lines:
# we don't need this here, because volume is read-only:
# Write-VolumeCache -DriveLetter $scriptDriveLetter
Set-Partition -DriveLetter $scriptDriveLetter -IsReadOnly $false
I've tested this script on my usb harddrive (fixed disk) with GPT(!) NTFS partition and it works. But, it DOESN'T work on usb removable drives as flash drives - you'll get an error from "Set-Partition"!
Also, about read-only usb flash drives. My test shows, that it is plausible... But:
- you can't set usb flash drive to read-only nor by "powershell" neither by "diskpart" by "attributes volume set readonly" - you'll get an error.
- to set drive to read-only it MUST be formatted as NTFS
- your usb flash drive MUST have GPT!
- it looks like setting an "read-only" attribute to a GPT NTFS volume just changes GPT's "read-only" attribute of the volume! So THAT's the key! And this can be easily achived through "diskpart": GPT ATTRIBUTES=0x1000000000000000!
To automate this, I wrote 3 scripts (they work only with usb removable drives! also, they auto-elevate themselves through UAC):
mk-USB-GPT-NTFS.ps1
# How to Run PowerShell Scripts with Administrative Privileges
# https://www.petri.com/run-powershell-scripts-with-administrative-privileges
param([switch]$Elevated)
function Check-Admin
{
$currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
$currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}
if ((Check-Admin) -eq $false)
{
if ($elevated)
{
# could not elevate, quit
}
else
{
Start-Process powershell.exe -Verb RunAs -ArgumentList ('-noprofile -ExecutionPolicy Bypass -file "{0}" -elevated' -f ($myinvocation.MyCommand.Definition))
}
exit
}
Get-Disk | Where-Object IsSystem -eq $False | Where-Object -FilterScript {$_.BusType -Eq "USB"}
echo ""
$USBDrive = Read-Host -Prompt 'Enter USB disk number (or press "Enter" for exit)'
if ([string]::IsNullOrEmpty($USBDrive)) { exit }
# select only non-system and USB drives
$USBDrive = Get-Disk | Where-Object IsSystem -eq $False | Where-Object -FilterScript {$_.BusType -Eq "USB"} | Where Number -eq $USBDrive
if (! ([string]::IsNullOrEmpty($USBDrive)) )
{
echo ""
echo $USBDrive
echo ""
$Answer = Read-Host -Prompt "Going to DELETE all partitions from this USB-drive and convert it to GPT with NTFS partition! Continue? (type 'Y')"
echo ""
if ("$Answer" -ceq "Y")
{
# "Initialize-Disk : The disk has already been initialized."
# https://social.technet.microsoft.com/Forums/en-US/1b1f2053-4e47-4d20-941a-d45ec9116dbe/initializedisk-the-disk-has-already-been-initialized?forum=winserversetup
# https://social.technet.microsoft.com/Forums/en-US/8e5916ef-60c1-4dac-9de2-18bd2984a0a5/initializedisk-the-disk-has-already-been-initialized?forum=winserverpowershell
# https://serverfault.com/questions/494848/cannot-initialize-disk-in-powershell-initialize-disk-throws-the-disk-has-alre/530679
#
# and only this helped:
# https://www.thomasmaurer.ch/2018/07/create-a-usb-drive-for-windows-server-2019-installation/
# "Set-Disk -PartitionStyle GPT", NOT "Initialize-Disk"!
# looks like "Initialize-Disk" doesn't work with USB/removable drives!
$USBDrive | Clear-Disk -RemoveData -RemoveOEM -Confirm:$false -PassThru
$USBDrive | Set-Disk -PartitionStyle GPT
$Volume = $USBDrive | New-Partition -UseMaximumSize -AssignDriveLetter | Format-Volume -FileSystem NTFS -Confirm:$false
}
echo ""
pause
}
USB_GPT_set_read-only.ps1
# https://docs.microsoft.com/en-us/powershell/module/storage/get-disk?view=win10-ps
# How to Run PowerShell Scripts with Administrative Privileges
# https://www.petri.com/run-powershell-scripts-with-administrative-privileges
param([switch]$Elevated)
function Check-Admin
{
$currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
$currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}
if ((Check-Admin) -eq $false)
{
if ($elevated)
{
# could not elevate, quit
}
else
{
Start-Process powershell.exe -Verb RunAs -ArgumentList ('-noprofile -ExecutionPolicy Bypass -file "{0}" -elevated' -f ($myinvocation.MyCommand.Definition))
}
exit
}
Get-Disk | Where-Object IsSystem -eq $False | Where-Object -FilterScript {$_.BusType -Eq "USB"}
echo ""
$USBDriveNumber = Read-Host -Prompt 'Enter USB disk number (or press "Enter" for exit)'
if ([string]::IsNullOrEmpty($USBDriveNumber)) { exit }
# select only non-system and USB drives
$USBDrive = Get-Disk | Where-Object IsSystem -eq $False | Where-Object -FilterScript {$_.BusType -Eq "USB"} | Where Number -eq $USBDriveNumber
if (! ([string]::IsNullOrEmpty($USBDrive)) )
{
# WARNING! there is NO check for partitions count > 1 on USB drives!
$DiskPartScriptFile = "$env:TMP\diskpart_USB_GPT_ATTRS.tmp"
$Letter = Get-Partition $USBDriveNumber | Select -ExpandProperty DriveLetter
echo ""
echo "Extra safety - flushing cache of volume '$Letter' ..."
Write-VolumeCache $Letter
echo ""
#echo "$USBDrive"
# "diskpart attributes volume set readonly" doesn't work on removable drives!
# so, we gonna do some magic - set "readonly" not by "volume attribute" but by GPT attribute :)
# GPT ATTRIBUTES=0x8000000000000000 is the default
# GPT ATTRIBUTES=0x1000000000000000 is "read-only"
"select disk $USBDriveNumber
select part 1
remove all dismount
GPT ATTRIBUTES=0x1000000000000000
assign" | Out-File -Encoding ascii $DiskPartScriptFile
# Execute the command, and print details
echo "diskpart /s $DiskPartScriptFile"
echo ""
Get-Content $DiskPartScriptFile
echo ""
diskpart /s $DiskPartScriptFile
del $DiskPartScriptFile
echo ""
pause
}
USB_GPT_enable_write.ps1
# https://docs.microsoft.com/en-us/powershell/module/storage/get-disk?view=win10-ps
# How to Run PowerShell Scripts with Administrative Privileges
# https://www.petri.com/run-powershell-scripts-with-administrative-privileges
param([switch]$Elevated)
function Check-Admin
{
$currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
$currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}
if ((Check-Admin) -eq $false)
{
if ($elevated)
{
# could not elevate, quit
}
else
{
Start-Process powershell.exe -Verb RunAs -ArgumentList ('-noprofile -ExecutionPolicy Bypass -file "{0}" -elevated' -f ($myinvocation.MyCommand.Definition))
}
exit
}
Get-Disk | Where-Object IsSystem -eq $False | Where-Object -FilterScript {$_.BusType -Eq "USB"}
echo ""
$USBDriveNumber = Read-Host -Prompt 'Enter USB disk number (or press "Enter" for exit)'
if ([string]::IsNullOrEmpty($USBDriveNumber)) { exit }
# select only non-system and USB drives
$USBDrive = Get-Disk | Where-Object IsSystem -eq $False | Where-Object -FilterScript {$_.BusType -Eq "USB"} | Where Number -eq $USBDriveNumber
if (! ([string]::IsNullOrEmpty($USBDrive)) )
{
# WARNING! there is NO check for partitions count > 1 on USB drives!
$DiskPartScriptFile = "$env:TMP\diskpart_USB_GPT_ATTRS.tmp"
# we don't need this here, because drive should be write-protected any way - nothing to flush :)
#$Letter = Get-Partition $USBDriveNumber | Select -ExpandProperty DriveLetter
#echo ""
#echo "Extra safety - flushing cache of volume '$Letter' ..."
#Write-VolumeCache $Letter
echo ""
#echo "$USBDrive"
# "diskpart attributes volume set readonly" doesn't work on removable drives!
# so, we gonna do some magic - set "readonly" not by "volume attribute" but by GPT attribute :)
# GPT ATTRIBUTES=0x8000000000000000 is the default
# GPT ATTRIBUTES=0x1000000000000000 is "read-only"
"select disk $USBDriveNumber
select part 1
remove all dismount
GPT ATTRIBUTES=0x8000000000000000
assign" | Out-File -Encoding ascii $DiskPartScriptFile
# Execute the command, and print details
echo "diskpart /s $DiskPartScriptFile"
echo ""
Get-Content $DiskPartScriptFile
echo ""
diskpart /s $DiskPartScriptFile
del $DiskPartScriptFile
echo ""
pause
}
Also forgot to mention:
- with GPT NTFS partition style, you'll also get read-only usb removable and fixed drives cross systems! Cool! (I've checked this on two different computers with Windows 8.1).
- to make sure that GPT "read-only" attribute is applied, script dismounts volume before that (so, you don't have to replug the device). So, make sure, you closed all your opened files from the drive before launching the script!
- after applying "read-only" attribute you can run into problem with safely removal of the drive - "device is busy". I think, that this is because of cached write data: drive is set to read-only (modified data wasn't flush in time), we try to eject device, OS tries to flush cached data, but the drive is already "read-only", so we get "device is busy" error. To minimize this, script tries to flush data right before sets volume to read-only.
Hi !
@lxandr : Can you please explain the usage and function of the first skript (mk-USB-GPT-NTFS.ps1).
Unfortunately I dont understand it and it seems a little bit risky for me to try without understanding . THANKS !!
BR BB10
@backbone10 sure! (strange, but your comment appeared here only now - months later, and so I didn't see it before, sorry).
First function "Check-Admin" checks if the script is run with elevated privileges. And if it's not, it tries to re-run itself with elevated privileges through UAC (elevation is needed to work with disk devices). If we remove this part from script, we must run this script with elevated privileges every time by hand. So this part of code just simplifies things - it's simpler to run script from user without administrative privileges - UAC window automatically appears and asks for user and password to elevate privileges.
Here we get a list of drives connected via usb (filtering all disks except usb) and print them:
Get-Disk | Where-Object IsSystem -eq $False | Where-Object -FilterScript {$_.BusType -Eq "USB"}
Then, we ask user to enter disk number from this list (in case more than one usb drive is connected):
$USBDrive = Read-Host -Prompt 'Enter USB disk number (or press "Enter" for exit)'
Then we make extra safety checks, and ask user if he sure to continue with deleting all partitions from that usb disk (Clear-Disk) and Creating GPT-style NTFS partition on it:
Set-Disk -PartitionStyle GPT, New-Partition ..., Format-Volume -FileSystem NTFS
I don’t know how this script will behave on windows 7 system - because of older default windows 7's PowerShell version.
I tested these scripts only on windows 8.1 64 bit. (so, on windows 10 they should work too).
It's better to test things under VirtualBox.
Nice script, you got there. Arrived here while searching for some issue I meet:
DISKPART
returns an error that states it's not possible to set read-only flag for amovible media (like flashdrive).Do you know a workaround?