-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Adding missing file closes #3807
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
internal/buffer/save.go
Outdated
| size, err := file.Write(b) | ||
| if err != nil { | ||
| err = util.OverwriteError{err, backupName} | ||
| file.Close() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. But why just here? What about all other error returns above, where we also leak file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, somehow missed it :/
Anyway, I moved the file opening closer to when we need it, instead of the beginning so now we always do a backup first, which I think is good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm guilty for not adding the file.Close() in the the err-paths above.
Having this backup could be unintentionally, because the user might not like having them at all (permbackup set to false). When the open fails right after and the user decides to drop that change anyway, then the user is faced with the error prompt next time opening this file.
Do we really want that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then the user is faced with the error prompt next time opening this file.
I don't have enough context, what do you mean exactly? Do we check for backup files (and warn the user?) when we open the same file again?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
micro/internal/buffer/backup.go
Lines 15 to 33 in 7492195
| const BackupMsg = `A backup was detected for: | |
| %s | |
| This likely means that micro crashed while editing this file, | |
| or another instance of micro is currently editing this file, | |
| or an error occurred while saving this file so it may be corrupted. | |
| The backup was created on %s and its path is: | |
| %s | |
| * 'recover' will apply the backup as unsaved changes to the current buffer. | |
| When the buffer is closed, the backup will be removed. | |
| * 'ignore' will ignore the backup, discarding its changes. The backup file | |
| will be removed. | |
| * 'abort' will abort the open operation, and instead open an empty buffer. | |
| Options: [r]ecover, [i]gnore, [a]bort: ` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see.
I have moved the backup part out so I don't have to put file close in so many places.
It now does roughly the same as before except it retains the backup if the file close failed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really want that?
We don't. As you might remember from #3273, the code structure is what it is, among other things, precisely because we wanted to avoid that.
2672aa8 to
c28c67e
Compare
internal/buffer/save.go
Outdated
| return err | ||
| } | ||
|
|
||
| func (b *Buffer) backupFile(path string) (string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
writeBackup()? backupFile() is not the most suitable name here (for example, because if backupFile() backs up a file, one would expect that backupDir() backs up a directory), right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, we are backing up a file, maybe we should rename backupDir() to getBackupDir() (or backupDirPath()) instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not the only function where we are backing up a file. We are also doing it in Backup() (because we create backups in more cases than when the user saves a file).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we should have a common function that performs the backup?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, because they are different kinds of backups, performed in different ways, as you can see from the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... it looks like we have a couple of bugs with saving with sudo:
🤦♂️
Looks like we've to store some more stuff for the withSudo-case and start the cmd in func (wf wrappedFile) Write() earliest.
Good hint regarding conv=fsync, this should do the trick.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we consider this as part of milestone v2.0.15?
From my perspective: yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like we've to store some more stuff for the
withSudo-case and start thecmdinfunc (wf wrappedFile) Write()earliest.
Which would mean that if sudo dd ... fails just because the user hasn't typed the correct password (so dd was not actually executed, so the file was not corrupted), we still write the backup, and don't remove it?
Should we consider this as part of milestone v2.0.15?
You mean the sudo issues? Those are not regressions (before #3273, save with sudo was not safe either, since any save was unsafe), so not necessarily?
Although in general, providing safe writes for sudo indeed seems no less important than providing safe writes in normal case (perhaps even more important, given that with sudo we have absolute power, and are able to corrupt any file in the filesystem).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which would mean that if
sudo dd ...fails just because the user hasn't typed the correct password (soddwas not actually executed, so the file was not corrupted), we still write the backup, and don't remove it?
No, it would require more logic, but anyway it is much easier and conv=notrunc,fsync (fsync only just to be sure) is all we need.
Although in general, providing safe writes for sudo indeed seems no less important than providing safe writes in normal case (perhaps even more important, given that with sudo we have absolute power, and are able to corrupt any file in the filesystem).
This is my concern too.
We should fix this, since the fix looks small and easy (-> #3814).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but anyway it is much easier
No it is not (unless you have an idea of something as easy as #3814 but actually working).
| if err != nil { | ||
| err = util.OverwriteError{err, backupName} | ||
| return size, err | ||
| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this just to avoid having err2? Why avoid that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
err2 replaces err anyway in the original.
Here, we are returning immediately if the write or close failed, for clarity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was asking why do you need this extra scope. I assumed you added it in order to be able to shadow the err variable at line 395. If so, why need to shadow it instead of keeping using another variable? If not, why need this extra scope?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The scope is just for clarity together with the comment.
If we failed inside this scope, we keep the backup (if any).
The err at 395 should be normal assignment instead, missed it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now I'm not sure this scope actually improves readability. You had to add this size := 0 which serves no purpose but to work around assigning size inside this scope...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a minor thing anyway, that extra size := 0 makes no difference.
The block is surrounded by forceKeepBackup and the comment applies pretty nicely to the whole block as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, no biggie.
internal/buffer/save.go
Outdated
| if err != nil { | ||
| file.Close() | ||
| err = util.OverwriteError{err, backupName} | ||
| return 0, err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can just return 0, util.OverwriteError{err, backupName}?
internal/buffer/save.go
Outdated
| return 0, err | ||
| } | ||
| } | ||
| b.forceKeepBackup = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why move setting forceKeepBackup here? We force keeping the backup in case we actually have successfully created this backup, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the original, we force backup (or rather retain the backup) if we failed to do backup or failed to write to the file so that we keep trying again later.
We are doing the same here except we also force the backup if we failed to close the file as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the original, we force backup (or rather retain the backup) if we failed to do backup
No we don't. In the original we don't set forceKeepBackup to true until we have successfully written the backup and are ready to start writing the original file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No we dont.
True, missed that. But if we failed to backup, then we should keep whatever backup was there no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would we? If we failed to backup, the backup file is a garbage, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I missed it but we are already removing it inside backupFile(). Should be fine then right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And then safeWrite() returns with an error and leaves forceKeepBackup set to true, which is not what we want, right?
becf2a3 to
ec01071
Compare
ec01071 to
be814fd
Compare
Switching between git branches on Windows sometimes fails due to opened files in micro.
Adding missing file closes fixes this.