问题
Hi ive got this exciting task, almost done actually.. but it fails in a funny aspect. The mission is to load a file with integers, sort them and write them to a file. Yay... Well my program is doing that, but it keeps the original content. ie:
lets say 45 32
Should get the ordered content 32 45
Well my program is keeping the original content and adds the new: 45 32 32 45.
So any sugestions on solving this? Though of deleting the original file and creating a new one in the same name. But thats kinda a failure, if the file has non-integers and my code has error reporting about that.
Ill give the important code here:
_OpenFile:
movq $2, %rax # open file
movq $inputfile, %rdi # filename
movq $2, %rsi # read and write
movq $0644, %rdx # setting proper permissions
syscall
ret
And:
_PrintNumber: #needs rdi as numberholder
movq $1, %r9 # count the number of chars to print
push $10 # store the chars on the stack, we always have '\n'
movq %rdi, %rax # things are easier with it in rax
movq $10, %rcx
decode_loop:
movq $0, %rdx
idivq %rcx # do rdx:rax / rcx
addq $48, %rdx # convert the remainder to an ASCII digit
pushq %rdx # and save it on the stack
addq $1, %r9 # while counting the number of chars
cmpq $0, %rax
jne decode_loop # loop until rax == 0
write_loop:
movq $1, %rax # write
movq $3, %rdi # to the file
movq %rsp, %rsi # which is stored here
movq $1, %rdx # a single character/byte
syscall
addq $8, %rsp # pop the character
addq $-1, %r9 # correct the char count
jne write_loop # loop until r9 reaches 0
ret
Thanks to all who would like to comment this!
回答1:
It looks like you're either re-opening the file with O_APPEND
, or you opened it read/write and didn't seek to the beginning before rewriting it. (So after reading the whole file, the position of the file descriptor is the end of the file, so newly-written data will go there.)
The lseek(2) system call is what you need to move the file position. lseek(fd, 0, SEEK_SET)
rewinds to the beginning. (Args go in EDI, RSI, EDX, like normal for the x86-64 System V system-call convention, and the kernel interface matches the libc interface.)
Since the data you'll be writing out has the same length, you don't need to ftruncate(fd, len)
the file before you start rewriting. You will overwrite all the bytes all the way to the end.
And BTW, you don't need to write
each character separately; you can make a small buffer containing all the ASCII bytes for a number and make one write
system call; much more efficient and actually takes less code: Printing an integer as a string with AT&T syntax, with Linux system calls instead of printf. My answer there also shows that you can #include <asm/unistd.h>
and use code likemov $__NR_write, %eax # SYS_write, from unistd_64.h
instead of using numeric literals for the system-call numbers, if you use a .S
file so gcc will run it through the preprocessor.
Unfortunately, most headers like <unistd.h>
(not asm/unistd.h
) have C declarations as well, so you can't as easily get macros for constants like SEEK_SET
or O_RDWR
that would let you do mov $SEEK_SET, %edx
or mov $O_WRONLY|O_CREAT|O_TRUNC, %esi
.
Unlinking the file would have no effect on the contents of the already-open file; to get an effect like what you're picturing in the question, you could close/reopen the file. (In Unix, removing the directory entry for a file doesn't affect programs that already have it open. It will be freed from disk once the last directory entry and file-descriptor for it are gone, though.)
So you'd open it for reading, read all the data, and then (after checking for errors, when you're sure you have some valid data to write), open(infile, O_CREAT|O_TRUNC|O_WRONLY, 0666)
and write out data. You didn't use O_APPEND
, so the position of the new write-only FD will be at the front of the file. And the file-size will be truncated to 0. Exactly like echo foo > filename
in the shell.
(It will still have the same inode number and be "the same file" with different contents unless you unlink(infile)
before opening it to re-create a new file of that name. In that case O_CREAT
is actually necessary. When reopening an existing file to write + truncate, O_CREAT
isn't needed when the file already exists.)
The key here is to check for read errors before doing anything destructive, not just read it, destroy the original, and then continue on. So the file is still there on disk while you're sorting.
来源:https://stackoverflow.com/questions/53125140/how-to-rewrite-a-files-content-linux-x86-64-assembly-gas