Disregarding the copy time, why not just find the 300M-th newline and move everything else down, then truncate?
One could write a tiny program to do that and I'd feel relatively safe running that.
(All that `dd` stuff would probably work too, and faster, but you'd need to spend time learning it first then. But that tool probably has discrepancies between OS'es, has history, etc. Can be simpler to just write the program.)
EDIT: and you can iterate in blocks of the size of the disregarded prefix.
memmove(beginning_of_file, beginning_of_file + 300_m_line_offset, file_size - 300_m_line_offset);