berthe
10th May 2005, 16:02
Hi,

I'm using the function seq.open(file, "a+") to open a specified will and to append some information. However, if an other user opened the specified file, I cannot open the file (return value = -13).

Is it possible to append some information to an opened file? Which function or parameter should I use?

NPRao
10th May 2005, 20:31
Berthe,
BaanERP Programmers Guide 7.3_a_sp1
13 EACCES - Permission denied
--------------------------------------------------------------------------------
Description:
This error indicates that you do not have permission to read, write or execute
the file.
Solution:
Check and modify the permissions of: The file that is mentioned in the error
message. The directory in which the file that is mentioned in the error message
is stored.
How did you create the file ? Did you use - creat.tmp.file$() ?
creat.tmp.file$()
--------------------------------------------------------------------------------
Syntax
string creat.tmp.file$( [string pathname] )

Description
This creates a temporary file. By default, the file is created in the current
directory. To create the file in a different directory, use the optional
pathname argument to specify the full path to the required directory.

In UNIX environments, a unique temporary file is created with UNIX-
privilege mode 0664.
If you specified the file name/creation yourself you have to change the File
Access Permissions so that the bsp group users can also write to the same file -
e = file.chmod(fname, S_IRWXU + S_IRWXG + S_IROTH)
Is it possible to append some information to an opened file?
Yes, you can. Copy and paste this prototype code into a 3-GL and execute for 2 different users.
The person who closes the file to flush/write from the buffer to the file on disk will update it first.

#pragma used dll ottdlldisplay
|******************************************************************************
function main()
{
long fp
string dtinfo(80), fname(128)

fname = bse.tmp.dir$() & "/np.log"
fp = seq.open(fname, "a+")
if fp < 0 then
mess("zmadms0036", 1, fname) |* File Opening Error: %1$s
exit(1)
endif
dtinfo = sprintf$(" %u(%02m/%02d/%04Y)-%U(%02h%x%02m%x%02s:%a) ", utc.num(), utc.num())
message("Hello " & logname$ & dtinfo)
e = seq.puts(logname$ & " " & dtinfo, fp)
e = seq.close(fp)
display.file(fname, "NP's Appended Log File", 80, 25, "", prog.name$, false)
}
|******************************************************************************

berthe
11th May 2005, 10:25
Hi NPRao,

The file is not created using creat.tmp.file because the file is an existing file, so it shouldn't be created again.

I tried to use
e = file.chmod(g.path.to, S_IRWXU + S_IRWXG + S_IROTH)
g.pointer.to = seq.open(g.path.to, "a+")
but g.pointer.to is still -13, so I cannot use the code you offered me.

If the file is not opened by an other user, seq.open(g.path.to, "a+") returns 3. So i have enough permissions to append information to the file.

I also tried to copy the existing file to a tmp file, adjust the tmp file and copy the tmp file to the existing file, but the last copy doesn't work: file.cp returns -1.

NPRao
11th May 2005, 20:35
Berthe,

It doesnt matter how the file is created (write/append/over-write modes etc). The file and directory ownerships/permission affect the operations. You can only change the file ownerships/group owner if only you are the owner or administrator/super-user.

Please post your complete code, makes it easy to debug.

I think this is related to the Windows OS File Handling. Refer to the articles -

PC Ext: Explanation of Opportunistic Locking on Windows NT (http://support.microsoft.com/default.aspx?scid=kb;EN-US;q129202)

Q: UNIX and Windows NT file access (http://answers.google.com/answers/threadview?id=17003)

Subject: Re: UNIX and Windows NT file access
Answered By: bookface-ga on 20 May 2002 04:58 PDT
Rated:
Both of the OSes handle this problem in the same way, though it is implemented slightly differently. Both use the same system for multiple programs that run concurrently, whether under one user or
multiple users.

Basically, programs declare that they want to use a file by a process known as locking the file; there are two forms of locking a file, read and write, and both can be done in either exclusive or shared mode.
Their declaration is put in the file descriptor, which is where information about the file is stored and accessed.

Generally it is preferred to use the shared mode of locking, called 'advisory locking' under *nix systems, versus the exclusive form of locking, termed 'mandatory locking' in *nix.

These modes are just as they sound; shared or advisory locking merely indicates that the file is in use and involved in some program or another; shared locks increment a counter in the file descriptor, and
leave it up to the next program trying to establish a lock to the file to process this information if it chooses (for instance, by waiting until all other programs have finished working with the file, or by
declaring another shared lock on the file).

Exclusive/Mandatory locking requires that no other programs access the file during the time the lock is established; it is generally unnecessary for files like documents, but it could be a vitally
important distinction, for system libraries in Windows for example. If a write lock is established in this mode, no other program may write to it; if a read lock is established this way, no other programs may
read from the file concurrently. When this happens, other processes may stop and wait, checking every once in a while, when they see the mandatory lock flag is set; or they can just give up on the file and
return some sort of an error or message to the user.

As a side note, Windows locking does not check properly for file permissions before attempting to lock a file, only when actually reading/writing data to the file. This presents a security risk, as a
malignent program could conceivably block access to all files on the disk by locking them exclusively. See the last link below for details.

As another aside, there is an alternative form of locking available under Windows, described in detail in the second link.

From:
http://www.ecst.csuchico.edu/~beej/guide/ipc/flock.html
"There are two types of locking mechanisms: mandatory and advisory. Mandatory systems will actually prevent read()s and write()s to file. Several Unix systems support them. Nevertheless, I'm going to ignore
them throughout this document, preferring instead to talk solely about advisory locks. With an advisory lock system, processes can still read and write from a file while it's locked. Useless? Not quite, since
there is a way for a process to check for the existence of a lock before a read or write. See, it's a kind of cooperative locking system. This is easily sufficient for almost all cases where file
locking is necessary."

From:
http://support.microsoft.com/default.aspx?scid=kb;EN-US;q129202
"With Exclusive Oplock, if a file is opened in a non-exclusive (deny none) mode, the redirector requests an opportunistic lock of the entire file. As long as no other process has the file open, the server
will grant this oplock, giving the redirector exclusive access to the specified file. This will allow the redirector to perform read-ahead, write-behind, and lock caching, as long as no other process tries to
open the file.

When a second process attempts to open the file, the original owner will be asked to Break Oplock or Break to Level II Oplock. At that point, the redirector must invalidate cached data, flush writes and
locks, and release the oplock, or close the file.

Opportunistic Locking level II, provides a method for granting read access to a file by more than one workstation, and these workstations can cache read data locally (read-ahead). As long as no station writes
to the file, multiple stations can have the file open with level II oplock."

From:
http://www.securiteam.com/windowsntfocus/6J00H0U3GW.html

"Applications can lock the file after file descriptor is open by application (or in the open() call itself). Usually there are two modes for locking - SHARED and EXCLUSIVE. A single application can put
the EXCLUSIVE lock on a file. If file is locked exclusively, no further locks can be put on the file by any another process. The main problem of the file locking mechanism is that it does not check for
any file permissions or the mode the file is opened with before locking is done. This makes it possible for an application with read-only (Under privileged) access to a file to lock it exclusively.
The way file locks interfere with file access depends on the particular OS. There are two possible situations: moderate and non-moderate file locks. *BSD and Linux use non-moderate locking,
while Windows NT locking is moderate. What does it mean? Under UNIX, file locking is only checked when another application tries to lock the file. If the application does not use file locking, it will not be
affected by file locking. Under Windows, things are different. If one application exclusively locks the file, another application cannot access this file even if it does not try to lock the file. This should
be treated as a design flaw, because the mechanism for file locking needs to interact security mechanism and verify the application's files permissions. This means that many security critical mechanisms under
Windows can be DoS'ed by file locking."

Finally, here's how it's handled with Python under Windows, very
similarly to the 'flock' method under Unix:
http://aspn.activestate.com/ASPN/Python/Reference/Products/ActivePython/PythonWin32Extensions/Windows_NT_Files_.2d.2d_Locking.html

Search Strategy:
http://www.google.com/search?q=file+locking+windows+write+read
http://www.google.com/search?q=read+lock+write+unix+file

Hope this was helpful. Please ask for clarification if necessary.

- bookface

berthe
12th May 2005, 11:20
Hi,

I don't think I can do that mutch with the file permissions...
If I run my code without the file specified in g.path.to opened, everything works file. If I run the code with the file specified in g.path.to opened, the execution stops after the second seq.open.
Here's my code (used to append the information specified in g.path.from in rtf format):

main()
{
g.lfn.from = seq.open(g.path.from, "r")
if g.lfn.from > 0 then
seq.gets(g.line.org, REPGEN_WIDTH, g.lfn.from)
else
| Message if seq.open failed
mess("ttadvs00101", 1, strip$(g.path.from), g.lfn.from)
endif

e = file.chmod(g.path.to, S_IRWXU + S_IRWXG + S_IROTH)
g.lfn.to = seq.open(g.path.to, "at+")
if g.lfn.to <= 0 then
mess("ttstpconv2", 1, g.path.to)
g.error.found = true
return(FALSE)
endif

convert.to.rtf()

g.return = seq.close(g.lfn.from)
g.return = seq.close(g.lfn.to)
e = file.chmod(g.path.to, S_IRWXU + S_IRWXG + S_IROTH)
}

function convert.to.rtf()
{
long l.index
long l.length
long l.char.asc2
long l.spaces
long l.underlined_on
long l.bold_on
string l.char.tmp(4) mb
string l.char.exp(4) mb

l.underlined_on = false
l.bold_on = false

l.index = 99999

repeat
if l.index > l.length then
g.return = seq.flush(g.lfn.to)
if l.index <> 99999 then
g.returns = seq.putc$(EOL, g.lfn.to)
endif

if seq.gets(g.line.org, REPGEN_WIDTH, g.lfn.from)
= 0 then
itttxt0009.tab.to.string(g.line.org, 0)
else
g.returns = seq.putc$("}", g.lfn.to)
g.returns = seq.putc$("}", g.lfn.to)
return
endif
l.length = len(g.line.org)
l.index = 1
g.return = seq.write("\par ", 5, g.lfn.to)
endif

l.char.tmp = g.line.org(l.index;1)

g.char.asc = asc(l.char.tmp)
l.index = l.index + 1

if g.char.asc >= 32 and | standard ascii characters
g.char.asc <= 126 then
g.char.column = g.char.column + 1
if g.char.asc = 92 or | Special character for RTF \{}
g.char.asc = 123 or
g.char.asc = 125 then
g.returns = seq.putc$("\", g.lfn.to)
endif
g.returns = seq.putc$(l.char.tmp, g.lfn.to)
continue
endif

if g.char.asc >= CF0 and | effects
g.char.asc <= CF15 then
on case g.char.asc
case CF0:
if l.bold_on then
g.return = seq.write("\b0 ", 4,
g.lfn.to)
l.bold_on = false
endif
if l.underlined_on then
g.return = seq.write("\ul0 ", 5,
g.lfn.to)
l.underlined_on = false
endif
break
case CF1:
case CF3:
case CF5:
case CF7:
g.return = seq.write("\b ", 3, g.lfn.to)
l.bold_on = true
break
case CF8:
case CF10:
case CF12:
case CF14:
g.return = seq.write("\ul ", 4, g.lfn.to)
l.underlined_on = true
break
case CF9:
case CF11:
case CF13:
case CF15:
g.return = seq.write("\b ", 3, g.lfn.to)
g.return = seq.write("\ul ", 4, g.lfn.to)
l.underlined_on = true
l.bold_on = true
break
case CF2: | Blinking not supported
case CF4: | Reverse not supported by RTF
case CF6: | Blinking and reverse not supported
break
endcase
endif

if g.char.asc = 9 then | horizontal tab
l.spaces = TAB.SPACES - (g.char.column \ TAB.SPACES)
g.return = seq.write(string.set$(" ", l.spaces),
l.spaces, g.lfn.to)
g.char.column = g.char.column + l.spaces
continue
endif

if g.char.asc = LF then | line feed
g.char.column = 0
g.returns = seq.putc$(l.char.tmp, g.lfn.to)
continue
endif

if g.char.asc = ESC then | fonts and ventura codes
l.char.asc2 = asc(g.line.org(l.index; 1))
if l.char.asc2 = SMALL.D or l.char.asc2 = NAK then
l.index = l.index + 1
l.char.asc2 = asc(g.line.org(l.index ;1))
on case l.char.asc2
case 33: |font 1
g.return = seq.write("\f2\fs16 ", 9, g.lfn.to)
break
case 34: |font 2
g.return = seq.write("\f8\fs15 ", 9, g.lfn.to)
break
case 35: |font 3
g.return = seq.write("\f8\fs20 ", 9, g.lfn.to)
break
case 36: |font 4
g.return = seq.write("\f8\fs20 ", 9, g.lfn.to)
break
case 37: |font 5
g.return = seq.write("\f8\fs20 ", 9, g.lfn.to)
break
case 38: |font 6
g.return = seq.write("\f8\fs20 ", 9, g.lfn.to)
break
case 39: |font 7
g.return = seq.write("\f8\fs20 ", 9, g.lfn.to)
break
case 40: |font 8
g.return = seq.write("\f8\fs20 ", 9, g.lfn.to)
break
case 41: |font 9
g.return = seq.write("\f0\fs15 ", 9, g.lfn.to)
break
case 42: |font 10
g.return = seq.write("\f0\fs20 ", 9, g.lfn.to)
break
case 43: |font 11
g.return = seq.write("\f0\fs30 ", 9, g.lfn.to)
break
case 44: |font 12
g.return = seq.write("\f7\fs15 ", 9, g.lfn.to)
break
case 45: |font 13
g.return = seq.write("\f7\fs20 ", 9, g.lfn.to)
break
case 46: |font 14
g.return = seq.write("\f7\fs30 ", 9, g.lfn.to)
break
case 47: |font 15
g.return = seq.write("\f6\fs15 ", 9, g.lfn.to)
break
case 48: |font 16
g.return = seq.write("\f6\fs20 ", 9, g.lfn.to)
break
case 49: |font 17
g.return = seq.write("\f6\fs30 ", 9, g.lfn.to)
break
case 50: |font 18
g.return = seq.write("\f5\fs15 ", 9, g.lfn.to)
break
case 51: |font 19
g.return = seq.write("\f5\fs20 ", 9, g.lfn.to)
break
case 52: |font 20
g.return = seq.write("\f5\fs30 ", 9, g.lfn.to)
break
case 53: |font 21
g.return = seq.write("\f4\fs15 ", 9, g.lfn.to)
break
case 54: |font 22
g.return = seq.write("\f3\fs20 ", 9, g.lfn.to)
break
case 55: |font 23
g.return = seq.write("\f3\fs30 ", 9, g.lfn.to)
break
case 56: |font 24
g.return = seq.write("\f0\fs30 ", 9, g.lfn.to)
break
endcase
l.index = l.index + 1
else
l.index = l.index + 1
endif
continue
endif

if g.char.asc >= LBT and | graphical characters
g.char.asc <= CROSS then
g.char.column = g.char.column + 1
write.symbol.to.rtf(g.char.asc)
continue
endif

if g.char.asc >= NBSP and | special characters
g.char.asc <= YDIA then
if g.char.asc <> HYPH then
write.iso.to.rtf(g.char.asc)
g.char.column = g.char.column + 1
continue
endif
endif

if mb.char(g.char.asc) = 2 then | Multi Byte characters
g.char.column = g.char.column + mb.width(l.char.tmp)
g.return = mb.export$(l.char.exp, l.char.tmp)
g.return = seq.write(l.char.exp,
len.in.bytes(l.char.exp), g.lfn.to)
continue
endif
until false
}