1 /* Numail Kit - general header for using the kit
3 ** Copyright 2001 Dr. Zoidberg Enterprises. All rights reserved.
7 #include <MailPrivate.h>
12 #include <Directory.h>
15 #include <FindDirectory.h>
17 #include <Messenger.h>
29 default_mail_directory()
32 if (find_directory(B_USER_DIRECTORY
, &path
) == B_OK
)
35 path
.SetTo("/boot/home/mail/");
42 default_mail_in_directory()
44 BPath path
= default_mail_directory();
52 default_mail_out_directory()
54 BPath path
= default_mail_directory();
62 WriteMessageFile(const BMessage
& archive
, const BPath
& path
, const char* name
)
68 BEntry settings_entry
;
70 bigtime_t now
= system_time();
72 create_directory(path
.Path(), 0777);
74 BDirectory
account_dir(path
.Path());
75 ret
= account_dir
.InitCheck();
78 fprintf(stderr
, "Couldn't open '%s': %s\n",
79 path
.Path(), strerror(ret
));
83 // get an entry for the tempfile
84 // Get it here so that failure doesn't create any problems
85 ret
= settings_entry
.SetTo(&account_dir
,leaf
.String());
88 fprintf(stderr
, "Couldn't create an entry for '%s/%s': %s\n",
89 path
.Path(), leaf
.String(), strerror(ret
));
95 // Save to a temporary file
98 // Our goal is to write to a tempfile and then use 'rename' to
99 // link that file into place once it contains valid contents.
100 // Given the filesystem's guarantee of atomic "rename" oper-
101 // ations this will guarantee that any non-temp files in the
102 // config directory are valid configuration files.
104 // Ideally, we would be able to do the following:
105 // BFile tmpfile(&account_dir, "tmpfile", B_WRITE_ONLY|B_CREATE_FILE);
107 // tmpfile.Relink(&account_dir,"realfile");
108 // But this doesn't work because there is no way in the API
109 // to link based on file descriptor. (There should be, for
110 // exactly this reason, and I see no reason that it can not
111 // be added to the API, but as it is not present now so we'll
112 // have to deal.) It has to be file-descriptor based because
113 // (a) all a BFile knows is its node/FD and (b) the file may
114 // be unlinked at any time, at which point renaming the entry
115 // to clobber the "realfile" will result in an invalid con-
116 // figuration file being created.
118 // We can't count on not clobbering the tempfile to gain
119 // exclusivity because, if the system crashes between when
120 // we create the tempfile an when we rename it, we will have
121 // a zombie tempfile that will prevent us from doing any more
124 // What we can do is:
126 // Create or open the tempfile
127 // // At this point no one will *clobber* the file, but
128 // // others may open it
130 // // At this point, no one else may open it and we have
131 // // exclusive access to it. Because of the above, we
132 // // know that our entry is still valid
134 // Truncate the tempfile
137 // Rename to the realfile
138 // // this does not affect the lock, but now open-
139 // // ing the realfile will fail with B_BUSY
142 // If this code is the only code that changes these files,
143 // then we are guaranteed that all realfiles will be valid
144 // settings files. I think that's the best we can do unless
145 // we get the Relink() api. An implementation of the above
151 while (system_time() - now
< timeout
) //-ATT-no timeout arg. Setting by #define
153 ret
= tmpfile
.SetTo(&settings_entry
, B_WRITE_ONLY
| B_CREATE_FILE
);
154 if (ret
!= B_BUSY
) break;
156 // wait 1/100th second
157 snooze((bigtime_t
)1e4
);
161 fprintf(stderr
, "Couldn't open '%s/%s' within the timeout period (%fs): %s\n",
162 path
.Path(), leaf
.String(), (float)timeout
/1e6
, strerror(ret
));
163 return ret
==B_BUSY
? B_TIMED_OUT
:ret
;
168 while (system_time() - now
< timeout
)
170 ret
= tmpfile
.Lock(); //-ATT-changed account_file to tmpfile. Is that allowed?
171 if (ret
!= B_BUSY
) break;
173 // wait 1/100th second
174 snooze((bigtime_t
)1e4
);
178 fprintf(stderr
, "Couldn't lock '%s/%s' in within the timeout period (%fs): %s\n",
179 path
.Path(), leaf
.String(), (float)timeout
/1e6
, strerror(ret
));
180 // Can't remove it here, since it might be someone else's.
181 // Leaving a zombie shouldn't cause any problems tho so
183 return ret
==B_BUSY
? B_TIMED_OUT
:ret
;
190 ret
= archive
.Flatten(&tmpfile
);
193 fprintf(stderr
, "Couldn't flatten settings to '%s/%s': %s\n",
194 path
.Path(), leaf
.String(), strerror(ret
));
198 // ensure it's actually writen
199 ret
= tmpfile
.Sync();
202 fprintf(stderr
, "Couldn't sync settings to '%s/%s': %s\n",
203 path
.Path(), leaf
.String(), strerror(ret
));
207 // clobber old settings
208 ret
= settings_entry
.Rename(name
,true);
211 fprintf(stderr
, "Couldn't clobber old settings '%s/%s': %s\n",
212 path
.Path(), name
, strerror(ret
));
220 } // namespace BPrivate