Archive: Win Vista/7: No writing in directories with spaces in the name


Win Vista/7: No writing in directories with spaces in the name
Hello!
I'm using the following piece of code to build my setup with NSIS. I built it on Windows XP and it works fine with Windows XP and 2000. But on Windows 7 and Vista, NSIS isn't able to write files to "C:\Program files\software name". But if I use a target directory wich doesn't include spaces, for example "C:\softwarename", it works.

Do you have any idea, why or what I shall do for get it working?

; Needs:
; http://nsis.sourceforge.net/ZipDLL_plug-in
!define MUI_ICON cookiepascal.ico
!define MUI_UNICON ${NSISDIR}\Contrib\Graphics\Icons\orange-uninstall.ico
!define MUI_HEADERIMAGE_BITMAP ${NSISDIR}\Contrib\Graphics\Header\orange.bmp
!define MUI_HEADERIMAGE_UNBITMAP ${NSISDIR}\Contrib\Graphics\Header\orange-uninstall.bmp
!define MUI_WELCOMEFINISHPAGE_BITMAP left.bmp
!define MUI_UNWELCOMEFINISHPAGE_BITMAP left-un.bmp
!define MUI_FINISHPAGE_NOAUTOCLOSE
!define MUI_UNFINISHPAGE_NOAUTOCLOSE

!include "MUI2.nsh"
!include "ZipDLL.nsh"

Name "Cookie Pascal"
OutFile "setup_win32.exe"

InstallDir "$PROGRAMFILES\CookiePascal"

RequestExecutionLevel user

!define MUI_ABORTWARNING

!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH

!insertmacro MUI_UNPAGE_WELCOME
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH

!insertmacro MUI_LANGUAGE "German"

!define MUI_WELCOMEPAGE_TITLE "Installation"

Function .onInit

!insertmacro MUI_LANGDLL_DISPLAY

FunctionEnd



section
setOutPath $INSTDIR
File "CookiePascal.exe"
File "cookiepascal.ico"
File "default-windows-config.conf"
File "bins.zip" ; contains:
; - Folder units/ with all FPC-units directly (e.g. subdir "rtl")
; - as.exe, cp.exe, cpp.exe, diff.exe, fp.exe, fp32.ico, fpc.cfg, fpc.exe, fpcmake.exe, fpcmkcfg.exe, fpcsubst.exe, fpdoc.exe, fpmc.exe,
; fppkg.exe, fprcp.exe, gcc.exe, gdate.exe, gdb.exe, gecho.exe, ginstall.exe, gmkdir.exe, grep.exe, ld.exe, make.exe, mv.exe, patch.exe,
; ppc386.exe, rm.exe, unzip.exe, upx.exe, zip.exe
!insertmacro ZIPDLL_EXTRACT "$INSTDIR\bins.zip" "$INSTDIR" "<ALL>"
Rename "$INSTDIR\default-windows-config.conf" "$INSTDIR\cookiepascal.conf"
writeUninstaller "$INSTDIR\uninstaller.exe"
delete "$INSTDIR\bins.zip"

WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\CookiePascal" \
"DisplayName" "Cookie Pascal" ;angezeigter Name
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\CookiePascal" \
"UninstallString" "$INSTDIR\uninstaller.exe"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\CookiePascal" \
"InstallLocation" "$INSTDIR"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\CookiePascal" \
"Publisher" "geek's factory"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\CookiePascal" \
"DisplayVersion" "1.0.0.0"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\CookiePascal" \
"NoModify" 0 ; Keine nachträgliche Modifizierung möglich
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\CookiePascal" \
"NoRepair" 1 ; Keine Reparatur möglich
WriteRegStr HKLM "Software\geeksfactory\CookiePascal" \
"Path" $INSTDIR ; Keine Reparatur möglich

sectionEnd

section "Desktop-Icons"
createShortCut "$DESKTOP\Cookie Pascal.lnk" "$INSTDIR\CookiePascal.exe"
sectionEnd

section "Startmenue-Eintrag"
createShortCut "$SMPROGRAMS\Cookie Pascal.lnk" "$INSTDIR\CookiePascal.exe"
sectionEnd

section "Schnellstartverknuepfung"
createShortCut "$QUICKLAUNCH\Cookie Pascal.lnk" "$INSTDIR\CookiePascal.exe"
sectionEnd

section "Uninstall"

delete "$INSTDIR\uninstaller.exe"
delete "$INSTDIR\CookiePascal.exe"
delete "$SMPROGRAMS\Cookie Pascal.lnk"
delete "$QUICKLAUNCH\Cookie Pascal.lnk"
delete "$DESKTOP\Cookie Pascal.lnk"
delete "$INSTDIR\cookiepascal.conf"
delete "$INSTDIR\cookiepascal.ico"
delete "$INSTDIR\as.exe"
delete "$INSTDIR\cp.exe"
delete "$INSTDIR\cpp.exe"
delete "$INSTDIR\diff.exe"
delete "$INSTDIR\fp.exe"
delete "$INSTDIR\fp32.ico"
delete "$INSTDIR\fpc.cfg"
delete "$INSTDIR\fpc.exe"
delete "$INSTDIR\fpcmake.exe"
delete "$INSTDIR\fpcmkcfg.exe"
delete "$INSTDIR\fpcsubst.exe"
delete "$INSTDIR\fpdoc.exe"
delete "$INSTDIR\fpmc.exe"
delete "$INSTDIR\fppkg.exe"
delete "$INSTDIR\fprcp.exe"
delete "$INSTDIR\gcc.exe"
delete "$INSTDIR\gdate.exe"
delete "$INSTDIR\gdb.exe"
delete "$INSTDIR\gecho.exe"
delete "$INSTDIR\ginstall.exe"
delete "$INSTDIR\gmkdir.exe"
delete "$INSTDIR\grep.exe"
delete "$INSTDIR\ld.exe"
delete "$INSTDIR\make.exe"
delete "$INSTDIR\mv.exe"
delete "$INSTDIR\patch.exe"
delete "$INSTDIR\ppc386.exe"
delete "$INSTDIR\rm.exe"
delete "$INSTDIR\unzip.exe"
delete "$INSTDIR\upx.exe"
delete "$INSTDIR\zip.exe"
DeleteRegKey HKLM Software\Microsoft\Windows\CurrentVersion\Uninstall\CookiePascal
RMDir $INSTDIR

sectionEnd

Raphael

RequestExecutionLevel user << needs to be admin
...you also need to make sure the user is admin by calling the userinfo plugin at some point (in .oninit for example)


Since I (and others) am having the same problems, I try to better understand:

1) with RequestExecution level set to user, no way of installing files to program files under vista/7. In my case, I have written a Photoshop script that should be put under "Program Files (x86)\Photoshop\...." folder.
Correct?

2) if installing files to program files subfolders is needed (my case), then the NSIS script must have on top

RequestExecutionLevel admin

Why should I also check the user privilege in onInit function?

Also to be able to freely write to and read from all the nested subfolders when the program is launched by a normal user, I should install the AccessControl plugin in NSIS and add to the script a
        AccessControl::GrantOnFile "$INSTDIR\subfolder" "Everyone" "FullAccess"

Correct?
Other things am I missing?

Thanks a lot!

Ciceti

Originally posted by Ciceti
Why should I also check the user privilege in onInit function?
Because windows ignores the requested execution level if UAC is disabled.

Originally posted by Ciceti
Also to be able to freely write to and read from all the nested subfolders when the program is launched by a normal user, I should install the AccessControl plugin in NSIS and add to the script a
        AccessControl::GrantOnFile "$INSTDIR\subfolder" "Everyone" "FullAccess"

Correct?
Other things am I missing?
Looks about right. But you really shouldn't open up a directory to all users like that. What if $INSTDIR is somewhere in Program Files? Then that subdir would become open to all users, some user does something stupid to put a virus into that open directory, and then a system admin might run the virus at admin level because he thinks "an application in program files is safe, because only an admin could have put it there."

Instead, you should program your application to write to LocalAppData or something like that.

Thanks, a couple of clarifications.

Originally posted by MSG
Because windows ignores the requested execution level if UAC is disabled.
... UAC is something that can be disabled by the user (sorry for stupid question)? So.. to be safe, what should I put into onInit?

Looks about right. But you really shouldn't open up a directory to all users like that. What if $INSTDIR is somewhere in Program Files? Then that subdir would become open to all users, some user does something stupid to put a virus into that open directory, and then a system admin might run the virus at admin level because he thinks "an application in program files is safe, because only an admin could have put it there."

Instead, you should program your application to write to LocalAppData or something like that.
Yes, my $INSTDIR is inside Program Files being a Photoshop plugin and I dont know how/if a plugin inside Photoshop can retrieve the LocalAppData, I will look for that. Otherwise can I give more restricted grants? I need write grants since the plugin saves a configuration file.

Thanks!!

Originally posted by Ciceti
UAC is something that can be disabled by the user (sorry for stupid question)? So.. to be safe, what should I put into onInit?
UAC can be disabled, yes. (Keep in mind that UAC is a Windows feature, and the UAC Plugin is an NSIS plugin. They're completely different things.)
In .onInit, you could do something like this:
  UserInfo::GetAccountType
Pop $1
${If} $1 != "Admin"
MessageBox MB_OK "Admin required."
quit
${EndIf}



Originally posted by Ciceti
Otherwise can I give more restricted grants? I need write grants since the plugin saves a configuration file.
Well yes, you can restrict as much as you like. To prevent giving 'all access' I currently use:
  FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE | DELETE
= STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE |
STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA |
STANDARD_RIGHTS_EXECUTE | FILE_EXECUTE | DELETE
= ReadControl + ReadData + ReadAttributes + ReadEA + Synchronize +
ReadControl + WriteData + WriteAttributes + WriteEA + AppendData +
ReadControl + Execute + Delete

But as you can see, if you give them that much, it makes little difference to just give it all. If they can write, they can destroy, so you might as well make it easy on them.

Thanks a lot.
For restriction of grants, I will try with "GenericRead + GenericWrite" that should enable writing/reading config files while preventing executing dangerous files. Do you agree?
Cheers


Originally posted by Ciceti
Thanks a lot.
For restriction of grants, I will try with "GenericRead + GenericWrite" that should enable writing/reading config files while preventing executing dangerous files. Do you agree?
Cheers
Sadly that won't solve anything. The danger is not a user executing something dangerous, the danger is an *admin* executing something dangerous. The only way to prevent that from happening is not allowing users to write to the directory. And that would mean modifying your plugin to write to localappdata/mydocs/etc (or not write at all).